GRAVI Pipeline Reference Manual  1.2.3
gravi_metrology.c
1 /* $Id: gravi_vis.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 
34 /*
35  *
36  * History :
37  * 12/11/2018 add gravi_data *static_param_data to gravi_metrology_telfc
38  * add gravi_data *static_param_data to gravi_metrology_get_fc_focus
39  * 10/01/2019 remove int old_flag_telescope[4][4][2]; int old_flag_fiber_coupler[4][2];
40  */
41 
42 /*-----------------------------------------------------------------------------
43  Includes
44  -----------------------------------------------------------------------------*/
45 
46 #ifdef HAVE_CONFIG_H
47 #include <config.h>
48 #endif
49 
50 #include <cpl.h>
51 #include <string.h>
52 #include <stdio.h>
53 #include <math.h>
54 #include <time.h>
55 
56 #include "gravi_data.h"
57 #include "gravi_pfits.h"
58 #include "gravi_dfs.h"
59 
60 #include "gravi_cpl.h"
61 #include "gravi_utils.h"
62 #include "gravi_ellipse.h"
63 #include "gravi_signal.h"
64 #include "gravi_eop.h"
65 
66 #include "gravi_metrology.h"
67 
68 /*-----------------------------------------------------------------------------
69  Private prototypes
70  -----------------------------------------------------------------------------*/
71 
72 
73 cpl_error_code gravi_metrology_tac (cpl_table * metrology_table,
74  cpl_table * vismet_table,
75  cpl_propertylist * header);
76 
77 cpl_error_code gravi_metrology_telfc (cpl_table * metrology_table,
78  cpl_table * vismet_table,
79  gravi_data *static_param_data,
80  cpl_propertylist * header);
81 
82 cpl_error_code gravi_metrology_acq (cpl_table * visacq_table,
83  cpl_table * vismet_table,
84  double delay,
85  cpl_propertylist * header);
86 
87 cpl_error_code gravi_metrology_update_receiverpos (cpl_propertylist * header,
88  cpl_table *receiver_table);
89 
90 double gravi_metrology_get_posx (cpl_propertylist * header,
91  int tel, int diode);
92 
93 double gravi_metrology_get_posy (cpl_propertylist * header,
94  int tel, int diode);
95 
96 double gravi_metrology_get_fc_focus (cpl_propertylist * header, int gv, gravi_data *static_param_data);
97 
98 double gravi_metrology_get_fc_shift (cpl_propertylist * header, int gv, gravi_data *default_focus_data);
99 
100 cpl_error_code gravi_metrology_get_astig (cpl_propertylist * header, int gv,
101  double * ampltiude, double * angle,
102  double * radius);
103 
104 long gravi_round (double number);
105 long gravi_round (double number)
106 {
107  return (number >= 0) ? (long)(number + 0.5) : (long)(number - 0.5);
108 }
109 
110 /*-----------------------------------------------------------------------------
111  TAC definitions
112  -----------------------------------------------------------------------------*/
113 
114 #define DEBUG_LEVEL 2
115 
116 #define PI 3.14159265359
117 #define TWOPI 6.28318530718
118 
119 #define MAX_DATA 1000000
120 #define MET_MAX_HISTORY 100
121 
122 #define FLAG_FLUX 1 /* error L on gui */
123 #define FLAG_SNR 2 /* error K on gui */
124 #define FLAG_UNWRAP 4 /* error J on gui */
125 #define FLAG_SPEED 8 /* error I on gui */
126 #define FLAG_JUMP 16 /* error H on gui */
127 #define FLAG_OVERLOAD 32 /* error G on gui */
128 #define FLAG_UNLOCKED 64 /* error F on gui */
129 #define FLAG_LASER_MV 128 /* error E on gui */
130 #define FLAG_LASER_MW 256 /* error D on gui */
131 #define FLAG_LASER_WAVE 512 /* error C on gui */
132 #define FLAG_VOLT 1024 /* error B on gui */
133 #define FLAG_PHASOR0 2048 /* error A on gui */
134 #define FLAG_LOST_PHASE 4096 /* error T on gui */
135 
136 #define FT 0
137 #define SC 1
138 #define BOTH 2
139 
140 #define SIN 0
141 #define COS 1
142 
143 #define NCOL 80
144 
145 /* Structure definitions */
146 
147 typedef struct {
148 
149  double min_allowed_flux_telescope[4][4][2]; /* minimum allowed flux per telescope and diode and side */
150  double min_allowed_flux_fiber_coupler[4][2]; /* minimum allowed flux per fiber coupler and side */
151  double min_allowed_snr_telescope[4][4][2]; /* minimum allowed SNR per telescope and diode and side */
152  double min_allowed_snr_fiber_coupler[4][2]; /* minimum allowed SNR per fiber coupler and side */
153  double min_allowed_snr_diff[4][4][2]; /* minimum allowed SNR per telescope and diode and side */
154  long number_to_average; /* how many cycles used for smoothing signal */
155  long number_for_rms; /* how many cycles used for calculation of rms */
156  long number_for_speed; /* how many cycles used for calculation of speed */
157  double nominal_wavelength; /* nominal wavelength of laser in nm */
158  double max_allowed_phase_difference; /* in rad */
159  double max_allowed_phase_speed; /* in rad per phase step */
160  double opl_base; /* proportionality constant between radian and meters */
161  double offset_volt_telescope[4][4][2][2]; /* offset voltage per telescope and diode and side and sin/cos */
162  double offset_volt_fiber_coupler[4][2][2]; /* offset voltage per fiber coupler and side and sin/cos */
163  double rms_flux_telescope[4][4][2]; /* rms of voltage per telescope and diode and side */
164  double rms_flux_fiber_coupler[4][2]; /* rms of voltage per fiber coupler and side */
165  double lockin_constant_telescope[4][4][2][2]; /* multiplicative constant for lockin voltage per telescope and diode and side and sin/cos */
166  double lockin_constant_fiber_coupler[4][2][2]; /* multiplicative constant for lockin voltage per fiber coupler and side and sin/cos */
167  double max_allowed_voltage;
168  double min_allowed_voltage;
169  long number_to_smooth_for_telescope; /* how many cycles are smoothed for difference Tel - FC phasor */
170  long sign_of_phase;
171  long check_for_jumps;
172  long calc_phase_speed;
173  double decrement_factor_speed; /* how much the weight of each speed value in buffer is decreased after a cycle */
174  double norm_speed; /* normalization factor for weighted speed adding */
175  double sigma_clip_speed; /* at what sigma level the speed predictor is clipped */
176 
177 } structTacConfiguration;
178 
179 
180 typedef struct {
181  long sample_number; /* Just a TAC loop counter : 0, 1, 2, ... */
182  long buffer_idx_avg;
183 
184  double volt_lockin_telescope[4][4][2][2]; /* per telescope and diode and side and sin/cos */
185  double sum_volt_lockin_telescope[4][4][2][2]; /* per telescope and diode and side and sin/cos */
186  double buffer_volt_lockin_telescope[4][4][2][2][MET_MAX_HISTORY]; /* per telescope and diode and side and sin/cos */
187  double flux_telescope[4][4][2];
188  double sum_flux_telescope[4][4][2]; /* 4 x 4 x FT/SC */
189  double sum_sq_flux_telescope[4][4][2]; /* 4 x 4 x FT/SC */
190  double buffer_flux_telescope[4][4][2][MET_MAX_HISTORY]; /* 4 x 4 x FT/SC */
191  double buffer_sq_flux_telescope[4][4][2][MET_MAX_HISTORY]; /* 4 x 4 x FT/SC */
192  double rms_flux_telescope[4][4][2]; /* 4 x 4 x FT/SC */
193  double snr_flux_telescope[4][4][2]; /* 4 x 4 x FT/SC */
194  double buffer_delta_phasor_telescope[4][4][2][2][MET_MAX_HISTORY];
195  double delta_phasor_telescope[4][4][2][2];
196  double best_estimate_phasor_telescope[4][4][2][2];
197  double best_estimate_flux_telescope[4][4][2];
198 
199  double buffer_delta_phase_telescope[4][4][2][MET_MAX_HISTORY];
200  double sum_delta_phase_telescope[4][4][2];
201  double buffer_sq_delta_phase_telescope[4][4][2][MET_MAX_HISTORY];
202  double sum_sq_delta_phase_telescope[4][4][2];
203 
204  double sum_speed_telescope[4][4][2];
205  double buffer_speed_telescope[4][4][2][MET_MAX_HISTORY];
206 
207  int flag_volt_telescope[4][4][2];
208  int flag_flux_telescope[4][4][2]; /* 4 x 4 x FT/SC */
209  int flag_snr_telescope[4][4][2]; /* 4 x 4 x FT/SC */
210 
211  double volt_lockin_fiber_coupler[4][2][2]; /* per fiber coupler and side and sin/cos */
212  double sum_volt_lockin_fiber_coupler[4][2][2]; /* per fiber coupler and side and sin/cos */
213  double buffer_volt_lockin_fiber_coupler[4][2][2][MET_MAX_HISTORY]; /* per fiber coupler and side and sin/cos */
214  double flux_fiber_coupler[4][2];
215  double sum_flux_fiber_coupler[4][2];
216  double sum_sq_flux_fiber_coupler[4][2];
217  double buffer_flux_fiber_coupler[4][2][MET_MAX_HISTORY];
218  double buffer_sq_flux_fiber_coupler[4][2][MET_MAX_HISTORY];
219  double rms_flux_fiber_coupler[4][2];
220  double snr_flux_fiber_coupler[4][2];
221  double buffer_delta_phasor_fiber_coupler[4][2][2][MET_MAX_HISTORY];
222  double delta_phasor_fiber_coupler[4][2][2];
223  double best_estimate_phasor_fiber_coupler[4][2][2];
224  double best_estimate_flux_fiber_coupler[4][2];
225  double buffer_best_estimate_phasor_fiber_coupler[4][2][2][MET_MAX_HISTORY];
226 
227  double buffer_delta_phase_fiber_coupler[4][2][MET_MAX_HISTORY];
228  double sum_delta_phase_fiber_coupler[4][2];
229  double buffer_sq_delta_phase_fiber_coupler[4][2][MET_MAX_HISTORY];
230  double sum_sq_delta_phase_fiber_coupler[4][2];
231 
232  double sum_speed_fiber_coupler[4][2];
233  double buffer_speed_fiber_coupler[4][2][MET_MAX_HISTORY];
234  double buffer_used_speed_fiber_coupler[4][2][MET_MAX_HISTORY];
235 
236  int flag_volt_fiber_coupler[4][2];
237  int flag_flux_fiber_coupler[4][2]; /* 4 fiber couplers, sin/cos */
238  int flag_snr_fiber_coupler[4][2]; /* 4 fiber couplers, sin/cos */
239 
240  double raw_phase_telescope[4][4][2]; /* 4 x 4 (tel, diode), FT/SC */
241  double prev_phase_telescope[4][4][2];
242  double unwrapped_phase_telescope[4][4][2];
243  int flag_unwrap_telescope[4][4][2];
244  int flag_jump_telescope[4][4][2];
245  int flag_speed_telescope[4][4][2];
246  int flag_phasor0_telescope[4][4][2];
247  double delta_phase_telescope[4][4]; /* FT - SC */
248  double mean_phase_telescope[4]; /* mean over tge 4 diodes */
249 
250  double raw_phase_fiber_coupler[4][2]; /* 4 fiber couplers , FT/SC */
251  double prev_phase_fiber_coupler[4][2];
252  double unwrapped_phase_fiber_coupler[4][2];
253  double buffer_unwrapped_phase_fiber_coupler[4][2][MET_MAX_HISTORY];
254  int flag_unwrap_fiber_coupler[4][2];
255  int flag_jump_fiber_coupler[4][2];
256  int flag_speed_fiber_coupler[4][2];
257  int flag_phasor0_fiber_coupler[4][2];
258  double delta_phase_fiber_coupler[4]; /* FT - SC */
259 
260  double raw_phase_diff[4][4][2]; /* difference: tel 1/2/3/4 minus FC */
261  double prev_phase_diff[4][4][2];
262  double unwrapped_phase_diff[4][4][2];
263  int flag_unwrap_diff[4][4][2];
264  int flag_phasor0_diff[4][4][2];
265  int flag_snr_diff[4][4][2];
266  int flag_flux_diff[4][4][2];
267 
268  double buffer_phasor_diff[4][4][2][2][MET_MAX_HISTORY]; /* in volt, tel diode, side, sin/cos, index */
269  double sum_phasor_diff[4][4][2][2]; /* in volt, tel diode, side, sin/cos */
270  double flux_diff[4][4][2]; /* phasor length in volt, tel, diode, side */
271  double sum_flux_diff[4][4][2]; /* 4 x 4 x FT/SC */
272  double sum_sq_flux_diff[4][4][2]; /* 4 x 4 x FT/SC */
273  double buffer_flux_diff[4][4][2][MET_MAX_HISTORY]; /* 4 x 4 x FT/SC */
274  double buffer_sq_flux_diff[4][4][2][MET_MAX_HISTORY]; /* 4 x 4 x FT/SC */
275  double rms_flux_diff[4][4][2]; /* 4 x 4 x FT/SC */
276  double snr_flux_diff[4][4][2]; /* 4 x 4 x FT/SC */
277 
278  int total_flag_telescope[4][4][2]; /* 4 x 4 (tel, diode), FT/SC */
279  int total_flag_fiber_coupler[4][2]; /* 4 fiber couplers , FT/SC */
280 
281  double opl_telescope[4]; /* diff FT-SC in meter */
282  double opl_telescope_diode[4][4]; /* diff FT-SC in meter */
283  double opl_fiber_coupler[4]; /* diff FT-SC in meter */
284  double opl_zero_telescope[4]; /* zero point in meter */
285  double opl_zero_telescope_diode[4][4]; /* zero point in meter */
286  double opl_zero_fiber_coupler[4]; /* zero point meter */
287 
288  double start_phase_fiber_coupler[4][2]; /* in rad */
289  double start_phase_telescope[4][4][2]; /* in rad */
290 
291  long freeze_count_fiber_coupler[4][2];
292  double freeze_phasor_fiber_coupler[4][2][2];
293  double freeze_speed_fiber_coupler[4][2];
294  double freeze_unwrapped_phase_fiber_coupler[4][2];
295  double used_unwrapped_phase_fiber_coupler[4][2];
296 
297  structTacConfiguration * tacConfiguration;
298 
299 } structTacData;
300 
301 /*-----------------------------------------------------------------------------
302  TAC prototypes
303  -----------------------------------------------------------------------------*/
304 
305 double metrology_sq(double x);
306 double myAtan(double x, double y, int* flag);
307 
308 structTacConfiguration * metrology_makeDefaultTacConfiguration (double lambda_met);
309 
310 structTacData * metrology_makeDefaultTacData (double lambda_met);
311 
312 int metrology_algorithm(structTacData * tacData);
313 
314 int metrology_unwrap(double raw_phase, double previous_phase, double max_allowed_phase_diff,
315  double *unwrapped_phase);
316 
317 int metrology_read_voltages(structTacData * tacData, double * volt);
318 
319 
320 /*-----------------------------------------------------------------------------
321  TAC function code
322  -----------------------------------------------------------------------------*/
323 
324 structTacConfiguration * metrology_makeDefaultTacConfiguration(double lambda_met)
325 {
326  structTacConfiguration *defaultTacConfiguration;
327 
328  defaultTacConfiguration = cpl_malloc((size_t) sizeof(structTacConfiguration));
329 
330  long tel, diode, side, comp, i;
331 
332  for (tel = 0; tel < 4; tel++) {
333  for(side = FT; side <= SC; side++) {
334  for (diode = 0; diode < 4; diode++) {
335  for (comp = SIN; comp <= COS; comp++) {
336  defaultTacConfiguration->offset_volt_telescope[tel][diode][side][comp] = 0.0;
337  defaultTacConfiguration->lockin_constant_telescope[tel][diode][side][comp] = 1.0;
338  }
339  defaultTacConfiguration->min_allowed_flux_telescope[tel][diode][side] = 0.01;
340  defaultTacConfiguration->min_allowed_snr_telescope[tel][diode][side] = 2.0;
341  defaultTacConfiguration->min_allowed_snr_diff[tel][diode][side] = 2.0;
342  defaultTacConfiguration->rms_flux_telescope[tel][diode][side] = 0.005;
343  }
344  for (comp = SIN; comp <= COS; comp++) {
345  defaultTacConfiguration->offset_volt_fiber_coupler[tel][side][comp] = 0.00;
346  defaultTacConfiguration->lockin_constant_fiber_coupler[tel][side][comp] = 1.0;
347  }
348  defaultTacConfiguration->min_allowed_flux_fiber_coupler[tel][side] = 0.01;
349  defaultTacConfiguration->min_allowed_snr_fiber_coupler[tel][side] = 2.0;
350  defaultTacConfiguration->rms_flux_fiber_coupler[tel][side] = 0.004;
351  }
352  }
353 
354  defaultTacConfiguration->min_allowed_voltage = -11.0;
355  defaultTacConfiguration->max_allowed_voltage = 11.0;
356 
357  defaultTacConfiguration->number_to_average = 20;
358  defaultTacConfiguration->number_to_average = 20;
359  defaultTacConfiguration->number_for_rms = 50;
360 
361  defaultTacConfiguration->max_allowed_phase_difference = TWOPI/3;
362  defaultTacConfiguration->max_allowed_phase_speed = TWOPI/4;
363 
364  defaultTacConfiguration->nominal_wavelength = lambda_met * 1e9;
365  defaultTacConfiguration->opl_base = defaultTacConfiguration->nominal_wavelength / 1e9 / TWOPI;
366 
367  defaultTacConfiguration->number_to_smooth_for_telescope = 50;
368  defaultTacConfiguration->sign_of_phase = 1;
369 
370  defaultTacConfiguration->check_for_jumps = 1;
371  defaultTacConfiguration->calc_phase_speed = 1;
372 
373  defaultTacConfiguration->number_for_speed = 3;
374  defaultTacConfiguration->decrement_factor_speed = 0.8;
375  defaultTacConfiguration->sigma_clip_speed = 0.0;
376 
377  defaultTacConfiguration->norm_speed = 0;
378  for(i=0; i<defaultTacConfiguration->number_for_speed; i++) {
379  defaultTacConfiguration->norm_speed += pow(defaultTacConfiguration->decrement_factor_speed,i);
380  }
381 
382  return defaultTacConfiguration;
383 }
384 
385 structTacData * metrology_makeDefaultTacData(double lambda_met)
386 {
387  long tel, diode, idx, side, comp;
388  structTacData *defaultTacData;
389 
390  defaultTacData = cpl_malloc((size_t) sizeof(structTacData));
391 
392  defaultTacData->tacConfiguration = metrology_makeDefaultTacConfiguration(lambda_met);
393 
394  defaultTacData->sample_number = 0;
395 
396  for (tel = 0; tel < 4; tel++) {
397  for(side = FT; side <= SC; side++) {
398  for (diode = 0; diode < 4; diode++) {
399  for (comp = SIN; comp <= COS; comp++) {
400  defaultTacData->volt_lockin_telescope[tel][diode][side][comp] = 0.0;
401  defaultTacData->sum_volt_lockin_telescope[tel][diode][side][comp] = 0.0;
402  defaultTacData->delta_phasor_telescope[tel][diode][side][comp] = 0.0;
403  defaultTacData->best_estimate_phasor_telescope[tel][diode][side][comp] = 0.0;
404  defaultTacData->sum_phasor_diff[tel][diode][side][comp] = 0.0;
405  }
406  defaultTacData->flux_telescope[tel][diode][side] = 0.0;
407  defaultTacData->rms_flux_telescope[tel][diode][side] = 0.0;
408  defaultTacData->snr_flux_telescope[tel][diode][side] = 0.0;
409  defaultTacData->sum_flux_telescope[tel][diode][side] = 0.0;
410  defaultTacData->sum_sq_flux_telescope[tel][diode][side] = 0.0;
411  defaultTacData->flag_flux_telescope[tel][diode][side] = 0;
412  defaultTacData->flag_snr_telescope[tel][diode][side] = 0;
413  defaultTacData->raw_phase_telescope[tel][diode][side] = 0.0;
414  defaultTacData->prev_phase_telescope[tel][diode][side] = 0.0;
415  defaultTacData->unwrapped_phase_telescope[tel][diode][side] = 0.0;
416  defaultTacData->flag_unwrap_telescope[tel][diode][side] = 0;
417  defaultTacData->flag_speed_telescope[tel][diode][side] = 0;
418  defaultTacData->flag_jump_telescope[tel][diode][side] = 0;
419  defaultTacData->flag_phasor0_telescope[tel][diode][side] = 0;
420  defaultTacData->total_flag_telescope[tel][diode][side] = 0;
421  defaultTacData->flag_volt_telescope[tel][diode][side] = 0;
422  defaultTacData->start_phase_telescope[tel][diode][side] = 0.0;
423  defaultTacData->best_estimate_flux_telescope[tel][diode][side] = 0.0;
424  defaultTacData->sum_speed_telescope[tel][diode][side] = 0.0;
425 
426  for (idx = 0; idx < MET_MAX_HISTORY; idx++) {
427  for (comp = SIN; comp <= COS; comp++) {
428  defaultTacData->buffer_volt_lockin_telescope[tel][diode][side][comp][idx] = 0.0;
429  defaultTacData->buffer_delta_phasor_telescope[tel][diode][side][comp][idx] = 0.0;
430  defaultTacData->buffer_phasor_diff[tel][diode][side][comp][idx] = 0.0;
431  }
432  defaultTacData->buffer_delta_phase_telescope[tel][diode][side][idx] = 0.0;
433  defaultTacData->buffer_sq_delta_phase_telescope[tel][diode][side][idx] = 0.0;
434  defaultTacData->buffer_flux_telescope[tel][diode][side][idx] = 0.0;
435  defaultTacData->buffer_sq_flux_telescope[tel][diode][side][idx] = 0.0;
436  defaultTacData->buffer_speed_telescope[tel][diode][side][idx] = 0.0;
437  defaultTacData->buffer_flux_diff[tel][diode][side][idx] = 0.0;
438  defaultTacData->buffer_sq_flux_diff[tel][diode][side][idx] = 0.0;
439  }
440  defaultTacData->flux_diff[tel][diode][side] = 0.0;
441  defaultTacData->rms_flux_diff[tel][diode][side] = 0.0;
442  defaultTacData->snr_flux_diff[tel][diode][side] = 0.0;
443  defaultTacData->sum_flux_diff[tel][diode][side] = 0.0;
444  defaultTacData->sum_sq_flux_diff[tel][diode][side] = 0.0;
445 
446  defaultTacData->sum_delta_phase_telescope[tel][diode][side] = 0.0;
447  defaultTacData->sum_sq_delta_phase_telescope[tel][diode][side] = 0.0;
448 
449  defaultTacData->raw_phase_diff[tel][diode][side] = 0.0;
450  defaultTacData->prev_phase_diff[tel][diode][side] = 0.0;
451  defaultTacData->unwrapped_phase_diff[tel][diode][side] = 0.0;
452  defaultTacData->flag_unwrap_diff[tel][diode][side] = 0;
453  defaultTacData->flag_phasor0_diff[tel][diode][side] = 0;
454  defaultTacData->flag_snr_diff[tel][diode][side] = 0;
455  } /* end loop diode */
456  for (comp = SIN; comp <= COS; comp++) {
457  defaultTacData->volt_lockin_fiber_coupler[tel][side][comp] = 0.0;
458  defaultTacData->sum_volt_lockin_fiber_coupler[tel][side][comp] = 0.0;
459  defaultTacData->delta_phasor_fiber_coupler[tel][side][comp] = 0.0;
460  defaultTacData->best_estimate_phasor_fiber_coupler[tel][side][comp] = 0.0;
461  defaultTacData->freeze_phasor_fiber_coupler[tel][side][comp] = 0.0;
462  }
463  defaultTacData->flux_fiber_coupler[tel][side] = 0.0;
464  defaultTacData->rms_flux_fiber_coupler[tel][side] = 0.0;
465  defaultTacData->snr_flux_fiber_coupler[tel][side] = 0.0;
466  defaultTacData->sum_flux_fiber_coupler[tel][side] = 0.0;
467  defaultTacData->sum_sq_flux_fiber_coupler[tel][side] = 0.0;
468  defaultTacData->flag_flux_fiber_coupler[tel][side] = 0;
469  defaultTacData->flag_snr_fiber_coupler[tel][side] = 0;
470  defaultTacData->raw_phase_fiber_coupler[tel][side] = 0.0;
471  defaultTacData->prev_phase_fiber_coupler[tel][side] = 0.0;
472  defaultTacData->unwrapped_phase_fiber_coupler[tel][side] = 0.0;
473  defaultTacData->flag_unwrap_fiber_coupler[tel][side] = 0;
474  defaultTacData->flag_jump_fiber_coupler[tel][side] = 0;
475  defaultTacData->flag_speed_fiber_coupler[tel][side] = 0;
476  defaultTacData->flag_phasor0_fiber_coupler[tel][side] = 0;
477  defaultTacData->total_flag_fiber_coupler[tel][side] = 0;
478  defaultTacData->flag_volt_fiber_coupler[tel][side] = 0;
479  defaultTacData->start_phase_fiber_coupler[tel][side] = 0.0;
480  defaultTacData->best_estimate_flux_fiber_coupler[tel][side] = 0.0;
481  defaultTacData->sum_speed_fiber_coupler[tel][side] = 0.0;
482  defaultTacData->freeze_count_fiber_coupler[tel][side] = 0;
483  defaultTacData->freeze_speed_fiber_coupler[tel][side] = 0.0;
484  defaultTacData->freeze_unwrapped_phase_fiber_coupler[tel][side] = 0.0;
485  defaultTacData->used_unwrapped_phase_fiber_coupler[tel][side] = 0.0;
486 
487  for (idx = 0; idx < MET_MAX_HISTORY; idx++) {
488  for (comp = SIN; comp <= COS; comp++) {
489  defaultTacData->buffer_volt_lockin_fiber_coupler[tel][side][comp][idx] = 0.0;
490  defaultTacData->buffer_delta_phasor_fiber_coupler[tel][side][comp][idx] = 0.0;
491  defaultTacData->buffer_best_estimate_phasor_fiber_coupler[tel][side][comp][idx] = 0.0;
492  }
493  defaultTacData->buffer_delta_phase_fiber_coupler[tel][side][idx] = 0.0;
494  defaultTacData->buffer_sq_delta_phase_fiber_coupler[tel][side][idx] = 0.0;
495  defaultTacData->buffer_flux_fiber_coupler[tel][side][idx] = 0.0;
496  defaultTacData->buffer_sq_flux_fiber_coupler[tel][side][idx] = 0.0;
497  defaultTacData->buffer_speed_fiber_coupler[tel][side][idx] = 0.0;
498  defaultTacData->buffer_used_speed_fiber_coupler[tel][side][idx] = 0.0;
499  defaultTacData->buffer_unwrapped_phase_fiber_coupler[tel][side][idx] = 0.0;
500  }
501  defaultTacData->sum_delta_phase_fiber_coupler[tel][side] = 0.0;
502  defaultTacData->sum_sq_delta_phase_fiber_coupler[tel][side] = 0.0;
503  } /* end loop over side */
504  defaultTacData->mean_phase_telescope[tel] = 0;
505 
506  defaultTacData->delta_phase_fiber_coupler[tel] = 0.0;
507  for (diode = 0; diode < 4; diode++) {
508  defaultTacData->delta_phase_telescope[tel][diode] = 0.0;
509  defaultTacData->opl_telescope_diode[tel][diode] = 0.0;
510  defaultTacData->opl_zero_telescope_diode[tel][diode] = 0.0;
511  }
512 
513  defaultTacData->opl_fiber_coupler[tel] = 0.0;
514  defaultTacData->opl_zero_fiber_coupler[tel] = 0.0;
515  defaultTacData->opl_telescope[tel] = 0.0;
516  defaultTacData->opl_zero_telescope[tel] = 0.0;
517 
518  } /* end loop over tel */
519 
520  return defaultTacData;
521 
522 }
523 
524 int metrology_read_voltages(structTacData * tacData, double * volt)
525 {
526  int err = 0;
527  int tel, diode, side, comp;
528  int idx = 0;
529  long buffer_idx_avg = tacData->buffer_idx_avg;
530  double sqflux, flux;
531  structTacConfiguration * tacConfiguration = tacData->tacConfiguration;
532  long buffer_idx_rms = ((tacData->sample_number - 1) % tacConfiguration->number_for_rms);
533 
534 
535  for (side = FT; side <= SC; side++) {
536  for (tel = 0; tel < 4; tel++) {
537  for (diode = 0; diode < 4; diode++) {
538  for (comp = SIN; comp <= COS; comp++) {
539  tacData->volt_lockin_telescope[tel][diode][side][comp] = volt[idx];
540  idx++;
541  }
542  }
543  }
544  }
545  for (side = FT; side <= SC; side++) {
546  for (tel = 0; tel < 4; tel++) {
547  for (comp = SIN; comp <= COS; comp++) {
548  tacData->volt_lockin_fiber_coupler[tel][side][comp] = volt[idx];
549  idx++;
550  }
551  }
552  }
553 
554  for (tel = 0; tel < 4; tel++) {
555  for (side = FT; side <= SC; side++) {
556  for (diode = 0; diode < 4; diode++) {
557  tacData->flag_volt_telescope[tel][diode][side] = 0;
558  if( tacData->volt_lockin_telescope[tel][diode][side][SIN] > tacConfiguration->max_allowed_voltage ||
559  tacData->volt_lockin_telescope[tel][diode][side][SIN] < tacConfiguration->min_allowed_voltage ||
560  tacData->volt_lockin_telescope[tel][diode][side][COS] > tacConfiguration->max_allowed_voltage ||
561  tacData->volt_lockin_telescope[tel][diode][side][COS] < tacConfiguration->min_allowed_voltage) {
562  tacData->flag_volt_telescope[tel][diode][side] = FLAG_VOLT;
563  }
564  tacData->volt_lockin_telescope[tel][diode][side][SIN] -=
565  tacConfiguration->offset_volt_telescope[tel][diode][side][SIN];
566  tacData->volt_lockin_telescope[tel][diode][side][SIN] *=
567  tacConfiguration->lockin_constant_telescope[tel][diode][side][SIN];
568 
569  tacData->volt_lockin_telescope[tel][diode][side][COS] -=
570  tacConfiguration->offset_volt_telescope[tel][diode][side][COS];
571  tacData->volt_lockin_telescope[tel][diode][side][COS] *=
572  tacConfiguration->lockin_constant_telescope[tel][diode][side][COS];
573 
574  }
575  tacData->flag_volt_fiber_coupler[tel][side] = 0;
576  if( tacData->volt_lockin_fiber_coupler[tel][side][SIN] > tacConfiguration->max_allowed_voltage ||
577  tacData->volt_lockin_fiber_coupler[tel][side][SIN] < tacConfiguration->min_allowed_voltage ||
578  tacData->volt_lockin_fiber_coupler[tel][side][COS] > tacConfiguration->max_allowed_voltage ||
579  tacData->volt_lockin_fiber_coupler[tel][side][COS] < tacConfiguration->min_allowed_voltage) {
580  tacData->flag_volt_fiber_coupler[tel][side] = FLAG_VOLT;
581  }
582  tacData->volt_lockin_fiber_coupler[tel][side][SIN] -=
583  tacConfiguration->offset_volt_fiber_coupler[tel][side][SIN];
584  tacData->volt_lockin_fiber_coupler[tel][side][SIN] *=
585  tacConfiguration->lockin_constant_fiber_coupler[tel][side][SIN];
586  tacData->volt_lockin_fiber_coupler[tel][side][COS] -=
587  tacConfiguration->offset_volt_fiber_coupler[tel][side][COS];
588  tacData->volt_lockin_fiber_coupler[tel][side][COS] *=
589  tacConfiguration->lockin_constant_fiber_coupler[tel][side][COS];
590  }
591  }
592  for (tel = 0; tel < 4; tel++) {
593  for (side = FT; side <= SC; side++) {
594  for (diode = 0; diode < 4; diode++) {
595 
596  tacData->buffer_volt_lockin_telescope[tel][diode][side][SIN][buffer_idx_avg] = tacData->volt_lockin_telescope[tel][diode][side][SIN];
597  tacData->buffer_volt_lockin_telescope[tel][diode][side][COS][buffer_idx_avg] = tacData->volt_lockin_telescope[tel][diode][side][COS];
598  sqflux = metrology_sq(tacData->volt_lockin_telescope[tel][diode][side][SIN]) + metrology_sq(tacData->volt_lockin_telescope[tel][diode][side][COS]);
599  flux=sqrt(sqflux);
600  tacData->flux_telescope[tel][diode][side] = flux;
601  tacData->sum_flux_telescope[tel][diode][side] -= tacData->buffer_flux_telescope[tel][diode][side][buffer_idx_rms];
602  tacData->sum_sq_flux_telescope[tel][diode][side] -= tacData->buffer_sq_flux_telescope[tel][diode][side][buffer_idx_rms];
603  tacData->buffer_flux_telescope[tel][diode][side][buffer_idx_rms] = flux;
604  tacData->buffer_sq_flux_telescope[tel][diode][side][buffer_idx_rms] = sqflux;
605  tacData->sum_flux_telescope[tel][diode][side] += flux;
606  tacData->sum_sq_flux_telescope[tel][diode][side] += sqflux;
607 
608  } /* end of loop over diodes */
609 
610  tacData->buffer_volt_lockin_fiber_coupler[tel][side][SIN][buffer_idx_avg] = tacData->volt_lockin_fiber_coupler[tel][side][SIN];
611  tacData->buffer_volt_lockin_fiber_coupler[tel][side][COS][buffer_idx_avg] = tacData->volt_lockin_fiber_coupler[tel][side][COS];
612  sqflux = metrology_sq(tacData->volt_lockin_fiber_coupler[tel][side][SIN]) + metrology_sq(tacData->volt_lockin_fiber_coupler[tel][side][COS]);
613  flux=sqrt(sqflux);
614  tacData->flux_fiber_coupler[tel][side] = flux;
615  tacData->sum_flux_fiber_coupler[tel][side] -= tacData->buffer_flux_fiber_coupler[tel][side][buffer_idx_rms];
616  tacData->sum_sq_flux_fiber_coupler[tel][side] -= tacData->buffer_sq_flux_fiber_coupler[tel][side][buffer_idx_rms];
617  tacData->buffer_flux_fiber_coupler[tel][side][buffer_idx_rms] = flux;
618  tacData->buffer_sq_flux_fiber_coupler[tel][side][buffer_idx_rms] = sqflux;
619  tacData->sum_flux_fiber_coupler[tel][side] += flux;
620  tacData->sum_sq_flux_fiber_coupler[tel][side] += sqflux;
621 
622  } /* end side loop */
623  } /* end telescope loop */
624 
625  return err;
626 }
627 
628 int metrology_unwrap(double raw_phase, double previous_phase, double max_allowed_phase_diff,
629  double *unwrapped_phase) {
630 
631  double previous_phase_mod, twopicount, diff;
632  int flag = 0;
633 
634  twopicount = floor(previous_phase / TWOPI); /* the number of full circles */
635  previous_phase_mod = previous_phase - TWOPI * twopicount; /* subtract the number of full circles to get the actual fraction of circle */
636 
637  if (previous_phase_mod > PI) { /* if the fraction is bigger the PI subtract 2PI to get the rest */
638  previous_phase_mod -= TWOPI;
639  }
640 
641  diff = raw_phase - previous_phase_mod;
642 
643  if (diff > PI) {
644  diff -= TWOPI;
645  } else if (diff < (-1.0 * PI)) {
646  diff += TWOPI;
647  }
648 
649  if (fabs(diff) > max_allowed_phase_diff) {
650  flag = FLAG_UNWRAP;
651  }
652 
653  *unwrapped_phase = (previous_phase + diff);
654 
655  return flag;
656 }
657 
658 double metrology_sq(double x) {
659  return x * x;
660 }
661 
662 double myAtan(double x, double y, int* flag)
663 {
664  if(x==0 && y == 0) {
665  *flag = FLAG_PHASOR0;
666  return 0.;
667  }
668  *flag = 0;
669  return atan2(y,x);
670 }
671 
672 
673 int metrology_algorithm(structTacData * tacData)
674 {
675  int err = 0;
676  long tel, diode, side;
677  long i, idx;
678  /* int old_flag_telescope[4][4][2];
679  int old_flag_fiber_coupler[4][2]; */
680  double x0, y0, x1, y1, xc, yc, delta_phase, sum_cos, sum_sin, new_phase, sq1, rms, tmp, speed;
681 
682  int flag, comb_flag, sum_flag;
683  double sqflux, flux;
684  double cos_delta_phase, sin_delta_phase;
685  double *x0p, *y0p;
686 
687  structTacConfiguration *tacConfiguration = tacData->tacConfiguration;
688  long sample_number = tacData->sample_number;
689 
690  long buffer_idx_smooth_tel =((sample_number - 1) % tacConfiguration->number_to_smooth_for_telescope);
691  long buffer_idx_avg =((sample_number - 1) % tacConfiguration->number_to_average);
692  long prev_idx_avg = buffer_idx_avg - 1;
693  if(prev_idx_avg < 0) {
694  prev_idx_avg += tacConfiguration->number_to_average;
695  }
696 
697  long buffer_idx_rms = ((sample_number - 1) % tacConfiguration->number_for_rms);
698  long buffer_idx_speed = ((sample_number - 1) % tacConfiguration->number_for_speed);
699 
700  long prev_idx_speed = buffer_idx_speed - 1;
701  if(prev_idx_speed < 0) {
702  prev_idx_speed += tacConfiguration->number_for_speed;
703  }
704 
705 
706  /* check if phasor did not move too far */
707 
708  if(sample_number > tacConfiguration->number_to_average) {
709  for (tel = 0; tel < 4; tel++) {
710  for (side = FT; side <= SC; side++) {
711  tacData->flag_jump_fiber_coupler[tel][side] = 0;
712  if((tacData->flux_fiber_coupler[tel][side] > tacConfiguration->min_allowed_flux_fiber_coupler[tel][side]) &&
713  (tacData->flux_fiber_coupler[tel][side] / tacConfiguration->rms_flux_fiber_coupler[tel][side] >
714  tacConfiguration->min_allowed_snr_fiber_coupler[tel][side]) &&
715  (tacData->snr_flux_fiber_coupler[tel][side] > tacConfiguration->min_allowed_snr_fiber_coupler[tel][side]) &&
716  (tacConfiguration->check_for_jumps == 1) ) {
717  x0 = tacData->volt_lockin_fiber_coupler[tel][side][COS];
718  y0 = tacData->volt_lockin_fiber_coupler[tel][side][SIN];
719  x1 = tacData->best_estimate_phasor_fiber_coupler[tel][side][COS];
720  y1 = tacData->best_estimate_phasor_fiber_coupler[tel][side][SIN];
721  xc = x1 * x0 + y1 * y0;
722  yc = x1 * y0 - y1 * x0;
723  if(fabs(myAtan(xc,yc,&flag)) > tacConfiguration->max_allowed_phase_difference) {
724  tacData->flag_jump_fiber_coupler[tel][side] = FLAG_JUMP;
725  x1 = tacData->buffer_volt_lockin_fiber_coupler[tel][side][COS][prev_idx_avg];
726  y1 = tacData->buffer_volt_lockin_fiber_coupler[tel][side][SIN][prev_idx_avg];
727  sq1 = sqrt(x1*x1+y1*y1);
728  if((sq1 > tacConfiguration->min_allowed_flux_fiber_coupler[tel][side]) &&
729  (sq1 / tacConfiguration->rms_flux_fiber_coupler[tel][side] >
730  tacConfiguration->min_allowed_snr_fiber_coupler[tel][side]) &&
731  (sq1 / tacData->rms_flux_fiber_coupler[tel][side] >
732  tacConfiguration->min_allowed_snr_fiber_coupler[tel][side]) ) {
733  xc = x1 * x0 + y1 * y0;
734  yc = x1 * y0 - y1 * x0;
735  if(fabs(myAtan(xc,yc,&flag)) < tacConfiguration->max_allowed_phase_difference) {
736  tacData->flag_jump_fiber_coupler[tel][side] = 0;
737  }
738  }
739  }
740  }
741  if(tacConfiguration->number_to_smooth_for_telescope < 0) {
742  for(diode = 0; diode < 4; diode++) {
743  if((tacData->flux_telescope[tel][diode][side] > tacConfiguration->min_allowed_flux_telescope[tel][diode][side]) &&
744  (tacData->flux_telescope[tel][diode][side] / tacConfiguration->rms_flux_telescope[tel][diode][side] >
745  tacConfiguration->min_allowed_snr_telescope[tel][diode][side]) &&
746  (tacData->snr_flux_telescope[tel][diode][side] > tacConfiguration->min_allowed_snr_telescope[tel][diode][side]) &&
747  (tacConfiguration->check_for_jumps == 1) ) {
748  tacData->flag_jump_telescope[tel][diode][side] = 0;
749  x0 = tacData->volt_lockin_telescope[tel][diode][side][COS];
750  y0 = tacData->volt_lockin_telescope[tel][diode][side][SIN];
751  x1 = tacData->best_estimate_phasor_telescope[tel][diode][side][COS];
752  y1 = tacData->best_estimate_phasor_telescope[tel][diode][side][SIN];
753  xc = x1 * x0 + y1 * y0;
754  yc = x1 * y0 - y1 * x0;
755 
756  if(fabs(myAtan(xc,yc,&flag)) > tacConfiguration->max_allowed_phase_difference) {
757  tacData->flag_jump_telescope[tel][diode][side] = FLAG_JUMP;
758  x1 = tacData->buffer_volt_lockin_telescope[tel][diode][side][COS][prev_idx_avg];
759  y1 = tacData->buffer_volt_lockin_telescope[tel][diode][side][SIN][prev_idx_avg];
760  sq1 = sqrt(x1*x1+y1*y1);
761  if((sq1 > tacConfiguration->min_allowed_flux_telescope[tel][diode][side]) &&
762  (sq1 / tacConfiguration->rms_flux_telescope[tel][diode][side] >
763  tacConfiguration->min_allowed_snr_telescope[tel][diode][side]) &&
764  (sq1 / tacData->rms_flux_telescope[tel][diode][side] >
765  tacConfiguration->min_allowed_snr_telescope[tel][diode][side])) {
766  xc = x1 * x0 + y1 * y0;
767  yc = x1 * y0 - y1 * x0;
768  if(fabs(myAtan(xc,yc,&flag)) < tacConfiguration->max_allowed_phase_difference) {
769  tacData->flag_jump_telescope[tel][diode][side] = 0;
770  }
771  }
772  }
773  }
774  }
775  } /* end if num_smooth_tel < 0 */
776  }
777  } /* end loop tel */
778  }
779 
780  /* raw phase calculation */
781 
782  for (tel = 0; tel < 4; tel++) {
783  for (side = FT; side <= SC; side++) {
784  x0 = tacData->volt_lockin_fiber_coupler[tel][side][COS];
785  y0 = tacData->volt_lockin_fiber_coupler[tel][side][SIN];
786  x0 += tacData->best_estimate_phasor_fiber_coupler[tel][side][COS];
787  y0 += tacData->best_estimate_phasor_fiber_coupler[tel][side][SIN];
788  tacData->raw_phase_fiber_coupler[tel][side] = myAtan(x0,y0,&(tacData->flag_phasor0_fiber_coupler[tel][side]));
789 
790  if(tacConfiguration->number_to_smooth_for_telescope < 0) {
791  for(diode = 0; diode < 4; diode++) {
792  x0 = tacData->volt_lockin_telescope[tel][diode][side][COS];
793  y0 = tacData->volt_lockin_telescope[tel][diode][side][SIN];
794  x0 += tacData->best_estimate_phasor_telescope[tel][diode][side][COS];
795  y0 += tacData->best_estimate_phasor_telescope[tel][diode][side][SIN];
796  tacData->raw_phase_telescope[tel][diode][side] = myAtan(x0,y0,&(tacData->flag_phasor0_telescope[tel][diode][side]));
797  }
798  }
799  }
800  }
801 
802  /* unwrapping of phase */
803 
804  for (tel = 0; tel < 4; tel++) {
805  for (side = FT; side <= SC; side++) {
806  if(sample_number == 1) {
807  tacData->prev_phase_fiber_coupler[tel][side] = tacData->raw_phase_fiber_coupler[tel][side];
808  } else {
809  tacData->prev_phase_fiber_coupler[tel][side] = tacData->unwrapped_phase_fiber_coupler[tel][side];
810  }
811 
812  tacData->flag_unwrap_fiber_coupler[tel][side] = metrology_unwrap(tacData->raw_phase_fiber_coupler[tel][side],
813  tacData->prev_phase_fiber_coupler[tel][side], tacConfiguration->max_allowed_phase_difference,
814  &tacData->unwrapped_phase_fiber_coupler[tel][side]);
815 
816  tacData->buffer_unwrapped_phase_fiber_coupler[tel][side][buffer_idx_avg] = tacData->unwrapped_phase_fiber_coupler[tel][side];
817 
818  if(sample_number < tacConfiguration->number_to_average) {
819  tacData->flag_unwrap_fiber_coupler[tel][side] = 0;
820  }
821 
822  if(tacConfiguration->number_to_smooth_for_telescope < 0) {
823  for(diode = 0; diode < 4; diode++) {
824  if(sample_number == 1) {
825  tacData->prev_phase_telescope[tel][diode][side] = tacData->raw_phase_telescope[tel][diode][side];
826  } else {
827  tacData->prev_phase_telescope[tel][diode][side] = tacData->unwrapped_phase_telescope[tel][diode][side];
828  }
829 
830  tacData->flag_unwrap_telescope[tel][diode][side] = metrology_unwrap(tacData->raw_phase_telescope[tel][diode][side],
831  tacData->prev_phase_telescope[tel][diode][side], tacConfiguration->max_allowed_phase_difference,
832  &tacData->unwrapped_phase_telescope[tel][diode][side]);
833 
834  if(sample_number < tacConfiguration->number_to_average) {
835  tacData->flag_unwrap_telescope[tel][diode][side] = 0;
836  }
837  }
838  }
839  }
840  }
841 
842  /* calculate next best estimate for phasor */
843 
844  for (tel = 0; tel < 4; tel++) {
845  for (side = FT; side <= SC; side++) {
846  if(tacConfiguration->number_to_average > 1) {
847  tacData->delta_phasor_fiber_coupler[tel][side][COS] -=
848  tacData->buffer_delta_phasor_fiber_coupler[tel][side][COS][buffer_idx_avg];
849  tacData->delta_phasor_fiber_coupler[tel][side][SIN] -=
850  tacData->buffer_delta_phasor_fiber_coupler[tel][side][SIN][buffer_idx_avg];
851 
852  x0 = tacData->buffer_volt_lockin_fiber_coupler[tel][side][COS][prev_idx_avg];
853  y0 = tacData->buffer_volt_lockin_fiber_coupler[tel][side][SIN][prev_idx_avg];
854  x1 = tacData->volt_lockin_fiber_coupler[tel][side][COS];
855  y1 = tacData->volt_lockin_fiber_coupler[tel][side][SIN];
856  xc = x1 * x0 + y1 * y0;
857  yc = x1 * y0 - y1 * x0;
858  tacData->buffer_delta_phasor_fiber_coupler[tel][side][COS][buffer_idx_avg] = xc;
859  tacData->buffer_delta_phasor_fiber_coupler[tel][side][SIN][buffer_idx_avg] = yc;
860 
861  tacData->delta_phasor_fiber_coupler[tel][side][COS] += xc;
862  tacData->delta_phasor_fiber_coupler[tel][side][SIN] += yc;
863 
864  tacData->sum_delta_phase_fiber_coupler[tel][side] -= tacData->buffer_delta_phase_fiber_coupler[tel][side][buffer_idx_avg];
865  tacData->sum_sq_delta_phase_fiber_coupler[tel][side] -= tacData->buffer_sq_delta_phase_fiber_coupler[tel][side][buffer_idx_avg];
866 
867  tacData->buffer_delta_phase_fiber_coupler[tel][side][buffer_idx_avg] = myAtan(yc, xc, &flag);
868  tacData->buffer_sq_delta_phase_fiber_coupler[tel][side][buffer_idx_avg] = metrology_sq(tacData->buffer_delta_phase_fiber_coupler[tel][side][buffer_idx_avg]);
869 
870  tacData->sum_delta_phase_fiber_coupler[tel][side] += tacData->buffer_delta_phase_fiber_coupler[tel][side][buffer_idx_avg];
871  tacData->sum_sq_delta_phase_fiber_coupler[tel][side] += tacData->buffer_sq_delta_phase_fiber_coupler[tel][side][buffer_idx_avg];
872 
873  rms = sqrt(tacData->sum_sq_delta_phase_fiber_coupler[tel][side]/tacConfiguration->number_to_average - metrology_sq(tacData->sum_delta_phase_fiber_coupler[tel][side]/tacConfiguration->number_to_average));
874 
875  tacData->sum_speed_fiber_coupler[tel][side] -= tacData->buffer_speed_fiber_coupler[tel][side][buffer_idx_speed] * pow(tacConfiguration->decrement_factor_speed, tacConfiguration->number_for_speed - 1);
876 
877  if(tacData->delta_phasor_fiber_coupler[tel][side][COS] == 0 && tacData->delta_phasor_fiber_coupler[tel][side][SIN] == 0) {
878  speed = tacData->buffer_speed_fiber_coupler[tel][side][prev_idx_speed];
879  } else {
880  speed = -myAtan(tacData->delta_phasor_fiber_coupler[tel][side][COS],tacData->delta_phasor_fiber_coupler[tel][side][SIN],&flag);
881  }
882 
883  if(tacConfiguration->calc_phase_speed == 0) speed = 0.0;
884 
885  if(tacConfiguration->sigma_clip_speed > 0) {
886  if(fabs(speed) * tacConfiguration->number_to_average < tacConfiguration->sigma_clip_speed * rms) speed = 0.0;
887  }
888 
889  tacData->buffer_speed_fiber_coupler[tel][side][buffer_idx_speed] = speed;
890  tacData->sum_speed_fiber_coupler[tel][side] *= tacConfiguration->decrement_factor_speed;
891  tacData->sum_speed_fiber_coupler[tel][side] += speed;
892 
893  delta_phase = tacData->sum_speed_fiber_coupler[tel][side] / tacConfiguration->norm_speed;
894 
895  } else {
896  delta_phase = 0.;
897  }
898 
899  tacData->buffer_used_speed_fiber_coupler[tel][side][buffer_idx_avg] = delta_phase;
900 
901  tacData->flag_speed_fiber_coupler[tel][side] = 0;
902  if(sample_number > tacConfiguration->number_to_average) {
903  if(fabs(delta_phase) > tacConfiguration->max_allowed_phase_speed) {
904  tacData->flag_speed_fiber_coupler[tel][side] = FLAG_SPEED;
905  }
906  }
907 
908  cos_delta_phase = cos(delta_phase);
909  sin_delta_phase = sin(delta_phase);
910 
911  x1 = 1;
912  y1 = 0;
913 
914  idx = buffer_idx_avg;
915  sum_cos = 0;
916  sum_sin = 0;
917  x0p = tacData->buffer_volt_lockin_fiber_coupler[tel][side][COS];
918  y0p = tacData->buffer_volt_lockin_fiber_coupler[tel][side][SIN];
919 
920  for(i = 0; i < tacConfiguration->number_to_average; i++) {
921  x0 = x0p[idx];
922  y0 = y0p[idx];
923  sum_cos += x0 * x1 - y0 * y1;
924  sum_sin += x0 * y1 + x1 * y0;
925  idx -= 1;
926  if(idx < 0) {
927  idx += tacConfiguration->number_to_average;
928  }
929  tmp = x1 * cos_delta_phase - y1 * sin_delta_phase;
930  y1 = x1 * sin_delta_phase + y1 * cos_delta_phase;
931  x1 = tmp;
932  }
933 
934  tacData->best_estimate_flux_fiber_coupler[tel][side] = sqrt(metrology_sq(sum_cos)+metrology_sq(sum_sin));
935  new_phase = myAtan(sum_cos, sum_sin, &flag) + delta_phase;
936 
937  tacData->best_estimate_phasor_fiber_coupler[tel][side][COS] = tacData->best_estimate_flux_fiber_coupler[tel][side] * cos(new_phase);
938  tacData->best_estimate_phasor_fiber_coupler[tel][side][SIN] = tacData->best_estimate_flux_fiber_coupler[tel][side] * sin(new_phase);
939  tacData->best_estimate_flux_fiber_coupler[tel][side] /= tacConfiguration->number_to_average;
940  tacData->buffer_best_estimate_phasor_fiber_coupler[tel][side][COS][buffer_idx_avg] = tacData->best_estimate_phasor_fiber_coupler[tel][side][COS]/ tacConfiguration->number_to_average;
941  tacData->buffer_best_estimate_phasor_fiber_coupler[tel][side][SIN][buffer_idx_avg] = tacData->best_estimate_phasor_fiber_coupler[tel][side][SIN]/ tacConfiguration->number_to_average;
942 
943  tacData->flag_flux_fiber_coupler[tel][side] = 0;
944  tacData->flag_snr_fiber_coupler[tel][side] = 0;
945  if (sample_number > tacConfiguration->number_to_average) {
946  if (tacData->best_estimate_flux_fiber_coupler[tel][side] < tacConfiguration->min_allowed_flux_fiber_coupler[tel][side]) {
947  tacData->flag_flux_fiber_coupler[tel][side] = FLAG_FLUX;
948  }
949  if (tacData->best_estimate_flux_fiber_coupler[tel][side] / tacConfiguration->rms_flux_fiber_coupler[tel][side] <
950  tacConfiguration->min_allowed_snr_fiber_coupler[tel][side]) {
951  tacData->flag_flux_fiber_coupler[tel][side] = FLAG_FLUX;
952  }
953  }
954 
955  if(tacConfiguration->number_to_smooth_for_telescope < 0) {
956  for(diode = 0; diode < 4; diode++) {
957  if(tacConfiguration->number_to_average > 1) {
958  tacData->delta_phasor_telescope[tel][diode][side][COS] -=
959  tacData->buffer_delta_phasor_telescope[tel][diode][side][COS][buffer_idx_avg];
960  tacData->delta_phasor_telescope[tel][diode][side][SIN] -=
961  tacData->buffer_delta_phasor_telescope[tel][diode][side][SIN][buffer_idx_avg];
962 
963  x0 = tacData->buffer_volt_lockin_telescope[tel][diode][side][COS][prev_idx_avg];
964  y0 = tacData->buffer_volt_lockin_telescope[tel][diode][side][SIN][prev_idx_avg];
965  x1 = tacData->volt_lockin_telescope[tel][diode][side][COS];
966  y1 = tacData->volt_lockin_telescope[tel][diode][side][SIN];
967  xc = x1 * x0 + y1 * y0;
968  yc = x1 * y0 - y1 * x0;
969  tacData->buffer_delta_phasor_telescope[tel][diode][side][COS][buffer_idx_avg] = xc;
970  tacData->buffer_delta_phasor_telescope[tel][diode][side][SIN][buffer_idx_avg] = yc;
971 
972  tacData->delta_phasor_telescope[tel][diode][side][COS] += xc;
973  tacData->delta_phasor_telescope[tel][diode][side][SIN] += yc;
974 
975  tacData->sum_delta_phase_telescope[tel][diode][side] -= tacData->buffer_delta_phase_telescope[tel][diode][side][buffer_idx_avg];
976  tacData->sum_sq_delta_phase_telescope[tel][diode][side] -= tacData->buffer_sq_delta_phase_telescope[tel][diode][side][buffer_idx_avg];
977 
978  tacData->buffer_delta_phase_telescope[tel][diode][side][buffer_idx_avg] = myAtan(yc, xc, &flag);
979  tacData->buffer_sq_delta_phase_telescope[tel][diode][side][buffer_idx_avg] = metrology_sq(tacData->buffer_delta_phase_telescope[tel][diode][side][buffer_idx_avg]);
980 
981  tacData->sum_delta_phase_telescope[tel][diode][side] += tacData->buffer_delta_phase_telescope[tel][diode][side][buffer_idx_avg];
982  tacData->sum_sq_delta_phase_telescope[tel][diode][side] += tacData->buffer_sq_delta_phase_telescope[tel][diode][side][buffer_idx_avg];
983 
984  rms = sqrt(tacData->sum_sq_delta_phase_telescope[tel][diode][side]/tacConfiguration->number_to_average - metrology_sq(tacData->sum_delta_phase_telescope[tel][diode][side]/tacConfiguration->number_to_average));
985 
986  tacData->sum_speed_telescope[tel][diode][side] -= tacData->buffer_speed_telescope[tel][diode][side][buffer_idx_speed] * pow(tacConfiguration->decrement_factor_speed, tacConfiguration->number_for_speed - 1);
987 
988  if(tacData->delta_phasor_telescope[tel][diode][side][COS] == 0 && tacData->delta_phasor_telescope[tel][diode][side][SIN] == 0) {
989  speed = tacData->buffer_speed_telescope[tel][diode][side][prev_idx_speed];
990  } else {
991  speed = -myAtan(tacData->delta_phasor_telescope[tel][diode][side][COS],tacData->delta_phasor_telescope[tel][diode][side][SIN],&flag);
992  }
993 
994  if(tacConfiguration->calc_phase_speed == 0) speed = 0.0;
995 
996  if(tacConfiguration->sigma_clip_speed > 0) {
997  if(fabs(speed) * tacConfiguration->number_to_average < tacConfiguration->sigma_clip_speed * rms) speed = 0.0;
998  }
999 
1000  tacData->buffer_speed_telescope[tel][diode][side][buffer_idx_speed] = speed;
1001  tacData->sum_speed_telescope[tel][diode][side] *= tacConfiguration->decrement_factor_speed;
1002  tacData->sum_speed_telescope[tel][diode][side] += speed;
1003 
1004  delta_phase = tacData->sum_speed_telescope[tel][diode][side] / tacConfiguration->norm_speed;
1005 
1006  } else {
1007  delta_phase = 0.;
1008  }
1009 
1010  tacData->flag_speed_telescope[tel][diode][side] = 0;
1011  if(sample_number > tacConfiguration->number_to_average) {
1012  if(fabs(delta_phase) > tacConfiguration->max_allowed_phase_difference) {
1013  tacData->flag_speed_telescope[tel][diode][side] = FLAG_SPEED;
1014  }
1015  }
1016 
1017  idx = buffer_idx_avg;
1018  sum_cos = 0;
1019  sum_sin = 0;
1020 
1021  cos_delta_phase = cos(delta_phase);
1022  sin_delta_phase = sin(delta_phase);
1023 
1024  x1 = 1;
1025  y1 = 0;
1026  x0p = tacData->buffer_volt_lockin_telescope[tel][diode][side][COS];
1027  y0p = tacData->buffer_volt_lockin_telescope[tel][diode][side][SIN];
1028 
1029  for(i = 0; i < tacConfiguration->number_to_average; i++) {
1030  x0 = x0p[idx];
1031  y0 = y0p[idx];
1032  sum_cos += x0 * x1 - y0 * y1;
1033  sum_sin += x0 * y1 + x1 * y0;
1034  idx -= 1;
1035  if(idx < 0) {
1036  idx += tacConfiguration->number_to_average;
1037  }
1038  tmp = x1 * cos_delta_phase - y1 * sin_delta_phase;
1039  y1 = x1 * sin_delta_phase + y1 * cos_delta_phase;
1040  x1 = tmp;
1041  }
1042 
1043  tacData->best_estimate_flux_telescope[tel][diode][side] = sqrt(metrology_sq(sum_cos)+metrology_sq(sum_sin));
1044  new_phase = myAtan(sum_cos, sum_sin, &flag) + delta_phase;
1045  tacData->best_estimate_phasor_telescope[tel][diode][side][COS] = tacData->best_estimate_flux_telescope[tel][diode][side] * cos(new_phase);
1046  tacData->best_estimate_phasor_telescope[tel][diode][side][SIN] = tacData->best_estimate_flux_telescope[tel][diode][side] * sin(new_phase);
1047  tacData->best_estimate_flux_telescope[tel][diode][side] /= tacConfiguration->number_to_average;
1048 
1049  tacData->flag_flux_telescope[tel][diode][side] = 0;
1050  tacData->flag_snr_telescope[tel][diode][side] = 0;
1051  if (sample_number > tacConfiguration->number_to_average) {
1052  if (tacData->best_estimate_flux_telescope[tel][diode][side] < tacConfiguration->min_allowed_flux_telescope[tel][diode][side]) {
1053  tacData->flag_flux_telescope[tel][diode][side] = FLAG_FLUX;
1054  }
1055  if (tacData->best_estimate_flux_telescope[tel][diode][side] / tacConfiguration->rms_flux_telescope[tel][diode][side] <
1056  tacConfiguration->min_allowed_snr_telescope[tel][diode][side]) {
1057  tacData->flag_flux_telescope[tel][diode][side] = FLAG_FLUX;
1058  }
1059  }
1060  } /* end loop diode */
1061  } /* end if nsmooth_tel < 0*/
1062  } /* end loop side */
1063  } /* end loop telescope */
1064 
1065  /* calclation of rms of flux and SNR check */
1066 
1067  for (tel = 0; tel < 4; tel++) {
1068  for (side = FT; side <= SC; side++) {
1069  if(tacConfiguration->number_to_smooth_for_telescope < 0) {
1070  for(diode = 0; diode < 4; diode++) {
1071  tacData->rms_flux_telescope[tel][diode][side] =
1072  sqrt(tacData->sum_sq_flux_telescope[tel][diode][side]/tacConfiguration->number_for_rms
1073  - metrology_sq(tacData->sum_flux_telescope[tel][diode][side]/tacConfiguration->number_for_rms));
1074  tacData->snr_flux_telescope[tel][diode][side] = tacData->best_estimate_flux_telescope[tel][diode][side] /
1075  tacData->rms_flux_telescope[tel][diode][side];
1076  if (sample_number > tacConfiguration->number_for_rms) {
1077  if (tacData->snr_flux_telescope[tel][diode][side] <
1078  tacConfiguration->min_allowed_snr_telescope[tel][diode][side]) {
1079  tacData->flag_snr_telescope[tel][diode][side] = FLAG_SNR;
1080  }
1081  }
1082  }
1083  }
1084  tacData->rms_flux_fiber_coupler[tel][side] =
1085  sqrt(tacData->sum_sq_flux_fiber_coupler[tel][side]/tacConfiguration->number_for_rms
1086  - metrology_sq(tacData->sum_flux_fiber_coupler[tel][side]/tacConfiguration->number_for_rms));
1087  tacData->snr_flux_fiber_coupler[tel][side] = tacData->best_estimate_flux_fiber_coupler[tel][side] /
1088  tacData->rms_flux_fiber_coupler[tel][side];
1089 
1090  if (sample_number > tacConfiguration->number_for_rms) {
1091  if (tacData->snr_flux_fiber_coupler[tel][side] <
1092  tacConfiguration->min_allowed_snr_fiber_coupler[tel][side]) {
1093  tacData->flag_snr_fiber_coupler[tel][side] = FLAG_SNR;
1094  }
1095  }
1096  }
1097  }
1098 
1099  /* phase calculation for difference phasor Tel - FC */
1100 
1101  if(tacConfiguration->number_to_smooth_for_telescope > 0) {
1102  for (tel = 0; tel < 4; tel++) {
1103  for (side = FT; side <= SC; side++) {
1104  x0 = tacData->volt_lockin_fiber_coupler[tel][side][COS];
1105  y0 = tacData->volt_lockin_fiber_coupler[tel][side][SIN];
1106  for (diode = 0; diode < 4; diode++) {
1107  x1 = tacData->volt_lockin_telescope[tel][diode][side][COS];
1108  y1 = tacData->volt_lockin_telescope[tel][diode][side][SIN];
1109  xc = x0 * x1 + y0 * y1;
1110  yc = x0 * y1 - y0 * x1;
1111 
1112  /* maintain a buffer of x and y values to be able to average */
1113  tacData->sum_phasor_diff[tel][diode][side][COS] -= tacData->buffer_phasor_diff[tel][diode][side][COS][buffer_idx_smooth_tel];
1114  tacData->sum_phasor_diff[tel][diode][side][SIN] -= tacData->buffer_phasor_diff[tel][diode][side][SIN][buffer_idx_smooth_tel];
1115  tacData->buffer_phasor_diff[tel][diode][side][COS][buffer_idx_smooth_tel] = xc;
1116  tacData->buffer_phasor_diff[tel][diode][side][SIN][buffer_idx_smooth_tel] = yc;
1117  tacData->sum_phasor_diff[tel][diode][side][COS] += xc;
1118  tacData->sum_phasor_diff[tel][diode][side][SIN] += yc;
1119  /* the flux of the difference is calculated as the sum, not the average ! */
1120  sqflux = metrology_sq(tacData->sum_phasor_diff[tel][diode][side][COS])+
1121  metrology_sq(tacData->sum_phasor_diff[tel][diode][side][SIN]);
1122  flux = sqrt(sqflux);
1123  tacData->flux_diff[tel][diode][side] = flux;
1124 
1125  tacData->raw_phase_diff[tel][diode][side] = myAtan(tacData->sum_phasor_diff[tel][diode][side][COS],
1126  tacData->sum_phasor_diff[tel][diode][side][SIN],
1127  &(tacData->flag_phasor0_diff[tel][diode][side]));
1128 
1129  tacData->sum_flux_diff[tel][diode][side] -= tacData->buffer_flux_diff[tel][diode][side][buffer_idx_rms];
1130  tacData->sum_sq_flux_diff[tel][diode][side] -= tacData->buffer_sq_flux_diff[tel][diode][side][buffer_idx_rms];
1131  tacData->buffer_flux_diff[tel][diode][side][buffer_idx_rms] = flux;
1132  tacData->buffer_sq_flux_diff[tel][diode][side][buffer_idx_rms] = sqflux;
1133  tacData->sum_flux_diff[tel][diode][side] += flux;
1134  tacData->sum_sq_flux_diff[tel][diode][side] += sqflux;
1135 
1136  tacData->rms_flux_diff[tel][diode][side] =
1137  sqrt(tacData->sum_sq_flux_diff[tel][diode][side]/tacConfiguration->number_for_rms
1138  - metrology_sq(tacData->sum_flux_diff[tel][diode][side]/tacConfiguration->number_for_rms));
1139  tacData->snr_flux_diff[tel][diode][side] = tacData->flux_diff[tel][diode][side] /
1140  tacData->rms_flux_diff[tel][diode][side];
1141 
1142  tacData->flag_flux_diff[tel][diode][side] = 0;
1143  tacData->flag_snr_diff[tel][diode][side] = 0;
1144  if (sample_number > tacConfiguration->number_to_smooth_for_telescope) {
1145  if (tacData->flux_diff[tel][diode][side] < tacConfiguration->min_allowed_flux_telescope[tel][diode][side]) {
1146  tacData->flag_flux_diff[tel][diode][side] = FLAG_FLUX;
1147  }
1148  if (tacData->flux_diff[tel][diode][side] / tacConfiguration->rms_flux_telescope[tel][diode][side] <
1149  tacConfiguration->min_allowed_snr_diff[tel][diode][side]) {
1150  tacData->flag_flux_diff[tel][diode][side] = FLAG_FLUX;
1151  }
1152  if(sample_number > tacConfiguration->number_for_rms) {
1153  if (tacData->snr_flux_diff[tel][diode][side] <
1154  tacConfiguration->min_allowed_snr_diff[tel][diode][side]) {
1155  tacData->flag_snr_diff[tel][diode][side] = FLAG_SNR;
1156  }
1157  }
1158  }
1159  } /* end loop diode */
1160  } /* end loop side */
1161  } /* end loop tel */
1162 
1163  /* unwrapping of difference phase */
1164  for (tel = 0; tel < 4; tel++) {
1165  for (side = FT; side <= SC; side++) {
1166  for (diode = 0; diode < 4; diode++) {
1167 
1168  if(sample_number <= tacConfiguration->number_to_smooth_for_telescope) {
1169  tacData->prev_phase_diff[tel][diode][side] = tacData->raw_phase_diff[tel][diode][side];
1170  } else {
1171  tacData->prev_phase_diff[tel][diode][side] = tacData->unwrapped_phase_diff[tel][diode][side];
1172  }
1173 
1174  tacData->flag_unwrap_diff[tel][diode][side] = metrology_unwrap(tacData->raw_phase_diff[tel][diode][side],
1175  tacData->prev_phase_diff[tel][diode][side], tacConfiguration->max_allowed_phase_difference,
1176  &tacData->unwrapped_phase_diff[tel][diode][side]);
1177 
1178  } /* end loop diode */
1179  } /* end loop side */
1180  } /* end loop tel */
1181  } /* end if num_smooth_tel > 0 */
1182 
1183 
1184  /* filling the telescope flags and phase from difference phasor */
1185  if(tacConfiguration->number_to_smooth_for_telescope > 0) {
1186  for (tel = 0; tel < 4; tel++) {
1187  for (side = FT; side <= SC; side++) {
1188  for (diode = 0; diode < 4; diode++) {
1189  tacData->flag_unwrap_telescope[tel][diode][side] = tacData->flag_unwrap_diff[tel][diode][side];
1190  tacData->flag_flux_telescope[tel][diode][side] = tacData->flag_flux_diff[tel][diode][side];
1191  tacData->flag_snr_telescope[tel][diode][side] = tacData->flag_snr_diff[tel][diode][side];
1192  tacData->flag_phasor0_telescope[tel][diode][side] = tacData->flag_phasor0_diff[tel][diode][side];
1193  tacData->unwrapped_phase_telescope[tel][diode][side] = tacData->unwrapped_phase_fiber_coupler[tel][side] + tacData->unwrapped_phase_diff[tel][diode][side];
1194  }
1195  }
1196  }
1197  } /* end if num_smooth_tel > 0 */
1198 
1199 
1200 
1201  /* Calculate the combined flags */
1202  for (tel = 0; tel < 4; tel++) {
1203  for (side = FT; side <= SC; side++) {
1204  comb_flag = 0;
1205  sum_flag = 0;
1206  for (diode = 0; diode < 4; diode++) {
1207  /* old_flag_telescope[tel][diode][side] = tacData->total_flag_telescope[tel][diode][side]; */
1208  tacData->total_flag_telescope[tel][diode][side] =
1209  tacData->flag_flux_telescope[tel][diode][side]
1210  + tacData->flag_unwrap_telescope[tel][diode][side]
1211  + tacData->flag_jump_telescope[tel][diode][side]
1212  + tacData->flag_speed_telescope[tel][diode][side]
1213  + tacData->flag_phasor0_telescope[tel][diode][side]
1214  + tacData->flag_snr_telescope[tel][diode][side]
1215  + tacData->flag_volt_telescope[tel][diode][side];
1216  comb_flag |= tacData->total_flag_telescope[tel][diode][side];
1217  if( tacData->total_flag_telescope[tel][diode][side] != 0) sum_flag++;
1218  } /* end loop diode */
1219 
1220  /* old_flag_fiber_coupler[tel][side] = tacData->total_flag_fiber_coupler[tel][side]; */
1221  tacData->total_flag_fiber_coupler[tel][side] =
1222  tacData->flag_flux_fiber_coupler[tel][side]
1223  + tacData->flag_unwrap_fiber_coupler[tel][side]
1224  + tacData->flag_jump_fiber_coupler[tel][side]
1225  + tacData->flag_speed_fiber_coupler[tel][side]
1226  + tacData->flag_phasor0_fiber_coupler[tel][side]
1227  + tacData->flag_snr_fiber_coupler[tel][side]
1228  + tacData->flag_volt_fiber_coupler[tel][side];
1229  } /* end loop side */
1230  } /* end loop tel */
1231 
1232  /* If there was an error, freeze + speed predict */
1233  for (tel = 0; tel < 4; tel++) {
1234  for (side = FT; side <= SC; side++) {
1235  if(tacData->total_flag_fiber_coupler[tel][side] != 0) {
1236  tacData->freeze_count_fiber_coupler[tel][side] += 1;
1237 
1238  if(tacData->freeze_count_fiber_coupler[tel][side] == 1) {
1239 
1240 
1241  tacData->freeze_phasor_fiber_coupler[tel][side][COS] = tacData->buffer_best_estimate_phasor_fiber_coupler[tel][side][COS][prev_idx_avg];
1242  tacData->freeze_phasor_fiber_coupler[tel][side][SIN] = tacData->buffer_best_estimate_phasor_fiber_coupler[tel][side][SIN][prev_idx_avg];
1243  tacData->freeze_speed_fiber_coupler[tel][side] = tacData->buffer_used_speed_fiber_coupler[tel][side][prev_idx_avg];
1244 
1245  if(tacConfiguration->calc_phase_speed == 0) tacData->freeze_speed_fiber_coupler[tel][side] = 0.0;
1246  if(tacData->flag_speed_fiber_coupler[tel][side] !=0) tacData->freeze_speed_fiber_coupler[tel][side] = 0.0;
1247  }
1248 
1249  new_phase = myAtan(tacData->freeze_phasor_fiber_coupler[tel][side][COS], tacData->freeze_phasor_fiber_coupler[tel][side][SIN], &(tacData->flag_phasor0_fiber_coupler[tel][side])) + tacData->freeze_speed_fiber_coupler[tel][side];
1250  tmp = sqrt(metrology_sq(tacData->freeze_phasor_fiber_coupler[tel][side][COS])+metrology_sq(tacData->freeze_phasor_fiber_coupler[tel][side][SIN]));
1251  tacData->freeze_phasor_fiber_coupler[tel][side][COS] = tmp * cos(new_phase);
1252  tacData->freeze_phasor_fiber_coupler[tel][side][SIN] = tmp * sin(new_phase);
1253 
1254  tmp = myAtan(tacData->freeze_phasor_fiber_coupler[tel][side][COS], tacData->freeze_phasor_fiber_coupler[tel][side][SIN], &(tacData->flag_phasor0_fiber_coupler[tel][side]));
1255 
1256  } else {
1257  /* this should be the nominal case, no error occurred and the counter is 0 */
1258 
1259  tacData->freeze_count_fiber_coupler[tel][side] = 0;
1260 
1261  tmp = myAtan(cos(tacData->unwrapped_phase_fiber_coupler[tel][side]), sin(tacData->unwrapped_phase_fiber_coupler[tel][side]), &(tacData-> flag_phasor0_fiber_coupler[tel][side]));
1262 
1263  if(tacData->flag_unwrap_fiber_coupler[tel][side] !=0 ) {
1264  printf("After freezing: speed predictor too far from where phase is, pred: %f, phase: %f\n",
1265  tacData->freeze_unwrapped_phase_fiber_coupler[tel][side],tacData->unwrapped_phase_fiber_coupler[tel][side]);
1266  }
1267  }
1268 
1269  if( metrology_unwrap(tmp,tacData->used_unwrapped_phase_fiber_coupler[tel][side],
1270  tacConfiguration->max_allowed_phase_difference,
1271  &tacData->used_unwrapped_phase_fiber_coupler[tel][side]) != 0) {
1272 
1273  if(sample_number >= tacConfiguration->number_to_average) {
1274 
1275  tacData->total_flag_fiber_coupler[tel][side] += FLAG_LOST_PHASE ;
1276 
1277  }
1278  }
1279 
1280  } /* loop side */
1281  } /* loop tel */
1282 
1283 
1284  /* difference FT - SC; and calculate mean per telescope */
1285 
1286  for (tel = 0; tel < 4; tel++) {
1287  tacData->mean_phase_telescope[tel] = 0.0;
1288  for (diode = 0; diode < 4; diode++) {
1289  tacData->delta_phase_telescope[tel][diode] = tacConfiguration->sign_of_phase * (tacData->unwrapped_phase_telescope[tel][diode][SC] - tacData->unwrapped_phase_telescope[tel][diode][FT]);
1290  tacData->mean_phase_telescope[tel] += tacData->delta_phase_telescope[tel][diode];
1291  }
1292  tacData->delta_phase_fiber_coupler[tel] = tacConfiguration->sign_of_phase * (tacData->used_unwrapped_phase_fiber_coupler[tel][SC] - tacData->used_unwrapped_phase_fiber_coupler[tel][FT]);
1293  tacData->mean_phase_telescope[tel] /= 4.0;
1294  }
1295 
1296 
1297  for (tel = 0; tel < 4; tel++) {
1298  for (diode = 0; diode < 4; diode++) {
1299  if ( tacData->total_flag_telescope[tel][diode][FT] == 0 && tacData->total_flag_telescope[tel][diode][SC] == 0) {
1300  tacData->opl_telescope_diode[tel][diode] = tacConfiguration->opl_base * tacData->delta_phase_telescope[tel][diode] - tacData->opl_zero_telescope_diode[tel][diode];
1301  }
1302  }
1303  tacData->opl_fiber_coupler[tel] = tacConfiguration->opl_base * tacData->delta_phase_fiber_coupler[tel]
1304  - tacData->opl_zero_fiber_coupler[tel];
1305 
1306  }
1307 
1308  return err;
1309 }
1310 
1311 /*-----------------------------------------------------------------------------
1312  Functions code
1313  -----------------------------------------------------------------------------*/
1314 
1315 /*----------------------------------------------------------------------------*/
1329 /*----------------------------------------------------------------------------*/
1330 
1331 cpl_error_code gravi_metrology_update_receiverpos (cpl_propertylist * header,
1332  cpl_table *receiver_table)
1333 {
1334  gravi_msg_function_start(1);
1335  cpl_ensure_code (header, CPL_ERROR_NULL_INPUT);
1336  cpl_ensure_code (receiver_table, CPL_ERROR_NULL_INPUT);
1337 
1338  /* Loop on telescope */
1339  for (int tel=0; tel<4; tel++) {
1340 
1341  const char * telname = gravi_conf_get_telname (tel, header);
1342  if (telname == NULL) {
1343  cpl_msg_warning (cpl_func,"Cannot update receiver position for tel %i", tel);
1344  continue;
1345  }
1346 
1347  /* Get row */
1348  cpl_size row;
1349  cpl_size nrow = cpl_table_get_nrow(receiver_table);
1350 
1351  for (row = 0; row<nrow; row++) {
1352  if (!strcmp (telname, cpl_table_get_string (receiver_table, "TEL_NAME", row) )) break;
1353  }
1354  cpl_ensure_code (row<nrow, CPL_ERROR_ILLEGAL_INPUT);
1355 
1356  /* Copy in header */
1357  for (int diode=0; diode<4; diode++) {
1358  char name[100];
1359 
1360  /* Set in header */
1361  double posx = gravi_table_get_value (receiver_table,"RECX",row,diode);
1362  sprintf (name, "ESO MET %s REC%iX", telname, diode+1);
1363  cpl_propertylist_update_double (header, name, posx);
1364 
1365  /* Set in header */
1366  double posy = gravi_table_get_value (receiver_table,"RECY",row,diode);
1367  sprintf (name, "ESO MET %s REC%iY", telname, diode+1);
1368  cpl_propertylist_update_double (header, name, posy);
1369 
1370  cpl_msg_info (cpl_func, "Update diode %i of %s: x=%.3f, y=%.3f", diode+1, telname, posx, posy);
1371  }
1372  }
1373 
1374  gravi_msg_function_exit(1);
1375  return CPL_ERROR_NONE;
1376 }
1377 
1378 /*----------------------------------------------------------------------------*/
1388 /*----------------------------------------------------------------------------*/
1389 
1390 double gravi_metrology_get_posx (cpl_propertylist * header,
1391  int tel, int diode)
1392 {
1393  gravi_msg_function_start(0);
1394  cpl_ensure (header, CPL_ERROR_NULL_INPUT, 0);
1395 
1396  /* Get telname */
1397  const char * telname = gravi_conf_get_telname (tel, header);
1398 
1399  if (telname == NULL) {
1400  cpl_msg_warning (cpl_func,"Cannot read receiver x-position for tel %i (set 0.0)", tel);
1401  return 0.0;
1402  }
1403 
1404  /* Read from header */
1405  char name[100];
1406  sprintf (name, "ESO MET %s REC%iX", telname, diode+1);
1407  double pos = cpl_propertylist_get_double (header, name)*1e-3;
1408 
1409  gravi_msg_function_exit(0);
1410  return pos;
1411 }
1412 
1413 double gravi_metrology_get_posy (cpl_propertylist * header,
1414  int tel, int diode)
1415 {
1416  gravi_msg_function_start(0);
1417  cpl_ensure (header, CPL_ERROR_NULL_INPUT, 0);
1418 
1419  /* Get telname */
1420  const char * telname = gravi_conf_get_telname (tel, header);
1421 
1422  if (telname == NULL) {
1423  cpl_msg_warning (cpl_func,"Cannot read receiver y-position for tel %i", tel);
1424  return 0.0;
1425  }
1426 
1427  /* Read from header */
1428  char name[100];
1429  sprintf (name, "ESO MET %s REC%iY", telname, diode+1);
1430  double pos = cpl_propertylist_get_double (header, name)*1e-3;
1431 
1432  gravi_msg_function_exit(0);
1433  return pos;
1434 }
1435 
1436 /*----------------------------------------------------------------------------*/
1445 /*----------------------------------------------------------------------------*/
1446 double gravi_metrology_get_fc_focus (cpl_propertylist * header, int gv, gravi_data *static_param_data)
1447 {
1448  gravi_msg_function_start(0);
1449  cpl_ensure (header, CPL_ERROR_NULL_INPUT, 0);
1450 // cpl_ensure (static_param_data, CPL_ERROR_NULL_INPUT, 0);
1451 
1452  /* Identify telescope name of requested GV input */
1453  const char * telname = gravi_conf_get_telname (3-gv, header);
1454  if (telname == NULL) {
1455  cpl_msg_warning (cpl_func,"Cannot read fiber coupler focus offset for GV%i", gv+1);
1456  return 0.0;
1457  }
1458 
1459  /* Assemble header keyword */
1460 // char name[100];
1461 // if (telname[0] == 'U') {
1462 // sprintf (name, "ESO MET GV%i UT FC FOCUS", gv+1);
1463 // } else {
1464 // sprintf (name, "ESO MET GV%i AT FC FOCUS", gv+1);
1465 // }
1466 
1467  /* If keyword available, read it, otherwise use defaults with warning */
1468  double defocus;
1469 
1470  /* ----------------- START EKW 12/11/2018 read constant parameter from calibration file */
1471  double defocus_at_default[4] = {-75.0, -100.0, 25.0, -75.0}; // Measured 2017-11-17
1472  double defocus_ut_default[4] = {-50.0, -125.0, -150.0, -175.0}; // Measured 2018-02-15
1473  double *defocus_at;
1474  double *defocus_ut;
1475 
1476  if (static_param_data)
1477  {
1478  cpl_table * default_focus_table = gravi_data_get_table (static_param_data, "FOCUSPAR");
1479 
1480  if ( cpl_table_has_column(default_focus_table , "fc_focus_at") ) {
1481  defocus_at = cpl_table_get_data_double (default_focus_table, "fc_focus_at");
1482  cpl_msg_info(cpl_func,"defocus_at [0] : %e \n",defocus_at[0] );
1483  cpl_msg_info(cpl_func,"defocus_at [1] : %e \n",defocus_at[1] );
1484  cpl_msg_info(cpl_func,"defocus_at [2] : %e \n",defocus_at[2] );
1485  cpl_msg_info(cpl_func,"defocus_at [3] : %e \n",defocus_at[3] );
1486  }
1487  else {
1488  cpl_msg_warning(cpl_func,"Cannot get the default values for fc_focus_at ");
1489  }
1490 
1491  if ( cpl_table_has_column(default_focus_table , "fc_focus_ut") ) {
1492  defocus_ut = cpl_table_get_data_double (default_focus_table, "fc_focus_ut");
1493  cpl_msg_info(cpl_func,"defocus_focus_ut [0] : %e \n",defocus_ut[0] );
1494  cpl_msg_info(cpl_func,"defocus_focus_ut [1] : %e \n",defocus_ut[1] );
1495  cpl_msg_info(cpl_func,"defocus_focus_ut [2] : %e \n",defocus_ut[2] );
1496  cpl_msg_info(cpl_func,"defocus_focus_ut [3] : %e \n",defocus_ut[3] );
1497  }
1498  else {
1499  cpl_msg_warning(cpl_func,"Cannot get the default values for fc_focus_ut ");
1500  }
1501  /* ------------------ END EKW 12/11/2018 read constant parameter from calibration file */
1502 
1503  cpl_msg_info (cpl_func,"Using static calibration for fiber coupler focus for GV%i!", gv+1);
1504  }
1505  else
1506  {
1507  defocus_at = defocus_at_default;
1508  defocus_ut = defocus_ut_default;
1509  cpl_msg_error (cpl_func,"Cannot find static calibration file, using hard-coded values for fiber coupler focus for GV%i!", gv+1);
1510  }
1511 
1512  if (telname[0] == 'U') {
1513  defocus = defocus_ut[gv];
1514  } else {
1515  defocus = defocus_at[gv];
1516  }
1517 
1518  gravi_msg_function_exit(0);
1519  return defocus*1e-9; // Convert from [nm] in header to [m] in pipeline
1520 }
1521 
1522 /*----------------------------------------------------------------------------*/
1532 /*----------------------------------------------------------------------------*/
1533 double gravi_metrology_get_fc_shift (cpl_propertylist * header, int gv, gravi_data *static_param_data)
1534 {
1535  gravi_msg_function_start(0);
1536  cpl_ensure (header, CPL_ERROR_NULL_INPUT, 0);
1537  //cpl_ensure (static_param_data, CPL_ERROR_NULL_INPUT, 0);
1538 
1539  /* Identify telescope name of requested GV input */
1540  const char * telname = gravi_conf_get_telname (3-gv, header);
1541  if (telname == NULL) {
1542  cpl_msg_warning (cpl_func,"Cannot read fiber coupler shift offset for GV%i", gv+1);
1543  return 0.0;
1544  }
1545 
1546  /* Assemble header keyword */
1547 // char name[100];
1548 // if (telname[0] == 'U') {
1549 // sprintf (name, "ESO MET GV%i UT FC SHIFT", gv+1);
1550 // } else {
1551 // sprintf (name, "ESO MET GV%i AT FC SHIFT", gv+1);
1552 // }
1553 
1554  /* ----------------- START EKW 12/11/2018 read constant parameter from calibration file */
1555  /* If keyword available, read it, otherwise use defaults with warning */
1556  double shift;
1557  double shift_ut_default[4] = {-450.0, -350.0, -50.0, -525.0}; // Measured 2018-02-15
1558  double shift_at_default[4] = {-100.0*1.8/8.0, 50.0*1.8/8.0, 150.0*1.8/8.0, -100.0*1.8/8.0}; // Measured 2017-11-17
1559 
1560  double *shift_at;
1561  double *shift_ut;
1562 
1563  if (static_param_data)
1564  {
1565  cpl_table * default_focus_table = gravi_data_get_table (static_param_data, "FOCUSPAR");
1566 
1567  if ( cpl_table_has_column(default_focus_table , "fc_focus_at") ) {
1568  shift_at = cpl_table_get_data_double (default_focus_table, "fc_focus_shift_at");
1569  cpl_msg_info(cpl_func,"shift_at [0] : %e \n",shift_at[0] );
1570  cpl_msg_info(cpl_func,"shift_at [1] : %e \n",shift_at[1] );
1571  cpl_msg_info(cpl_func,"shift_at [2] : %e \n",shift_at[2] );
1572  cpl_msg_info(cpl_func,"shift_at [3] : %e \n",shift_at[3] );
1573  }
1574  else {
1575  cpl_msg_warning(cpl_func,"Cannot get the default values for fc_focus_shift_at ");
1576  }
1577 
1578  if ( cpl_table_has_column(default_focus_table , "fc_focus_ut") ) {
1579  shift_ut = cpl_table_get_data_double (default_focus_table, "fc_focus_shift_ut");
1580  cpl_msg_info(cpl_func,"shift_ut [0] : %e \n", shift_ut[0] );
1581  cpl_msg_info(cpl_func,"shift_ut [1] : %e \n", shift_ut[1] );
1582  cpl_msg_info(cpl_func,"shift_ut [2] : %e \n", shift_ut[2] );
1583  cpl_msg_info(cpl_func,"shift_ut [3] : %e \n", shift_ut[3] );
1584  }
1585  else {
1586  cpl_msg_warning(cpl_func,"Cannot get the default values for fc_focus_shift_ut ");
1587  }
1588  /* ------------------ END EKW 12/11/2018 read constant parameter from calibration file */
1589 
1590  cpl_msg_info (cpl_func,"Using static calibration for fiber coupler shift for GV%i!", gv+1);
1591  }
1592  else
1593  {
1594  shift_at = shift_at_default;
1595  shift_ut = shift_ut_default;
1596  cpl_msg_error (cpl_func,"Cannot find static calibration file, using hard-coded values for fiber coupler focus shift for GV%i!", gv+1);
1597  }
1598 
1599  if (telname[0] == 'U') {
1600  shift = shift_ut[gv];
1601  } else {
1602  shift = shift_at[gv];
1603  }
1604 
1605  gravi_msg_function_exit(0);
1606  return shift*1e-9; // Convert from [nm/arcsec] in header to [m/arcsec] in pipeline
1607 }
1608 
1609 /*----------------------------------------------------------------------------*/
1620 /*----------------------------------------------------------------------------*/
1621 cpl_error_code gravi_metrology_get_astig (cpl_propertylist * header, int gv,
1622  double * amplitude, double * angle,
1623  double * radius)
1624 {
1625  gravi_msg_function_start(0);
1626  cpl_ensure (header, CPL_ERROR_NULL_INPUT, 0);
1627 
1628  /* Identify telescope name of requested GV input */
1629  const char * telname = gravi_conf_get_telname (3-gv, header);
1630  if (telname == NULL) {
1631  cpl_msg_warning (cpl_func,"Cannot read the astigmatism offset for GV%i", gv+1);
1632  *amplitude = 0.0;
1633  *angle = 0.0;
1634  *radius = 1.0;
1635  return CPL_ERROR_NONE;
1636  }
1637 
1638  /* Assemble header keywords */
1639  char name_amp[100], name_ang[100];
1640  if (telname[0] == 'U') {
1641  sprintf (name_amp, "ESO MET GV%i UT ASTIG AMP", gv+1);
1642  sprintf (name_ang, "ESO MET GV%i UT ASTIG ANG", gv+1);
1643  *radius = 4.0; // radius of an UT [m]
1644  } else {
1645  sprintf (name_amp, "ESO MET GV%i AT ASTIG AMP", gv+1);
1646  sprintf (name_ang, "ESO MET GV%i AT ASTIG ANG", gv+1);
1647  *radius = 0.9; // radius if an AT [m]
1648  }
1649 
1650  /* If keywords available, read it, otherwise use defaults with warning */
1651  /* These are given in telescope order */
1652  const double amplitude_at[4] = {164.37970, 166.604301, 99.612594, 266.071934}; // [nm]
1653  const double amplitude_ut[4] = {182.16255, 185.116601, 113.190052, 242.351495}; // [nm]
1654  const double angle_at[4] = { 1.116211914, 28.48113853, 0.42385066, 25.92291209}; // [deg]]
1655  const double angle_ut[4] = {-2.696882009, 18.07496983, 20.56624745, 19.13334754}; // [deg]
1656  if (cpl_propertylist_has(header, name_amp)
1657  && cpl_propertylist_has(header, name_ang)) {
1658  *amplitude = cpl_propertylist_get_double (header, name_amp);
1659  *angle = cpl_propertylist_get_double (header, name_ang);
1660  } else {
1661  cpl_msg_warning (cpl_func,"Using static calibration for astigmatism offset for GV%i!", gv+1);
1662  if (telname[0] == 'U') {
1663  *amplitude = amplitude_ut[3-gv];
1664  *angle = angle_ut[3-gv];
1665  } else {
1666  *amplitude = amplitude_at[3-gv];
1667  *angle = angle_at[3-gv];
1668  }
1669  }
1670 
1671  gravi_msg_function_exit(0);
1672  return CPL_ERROR_NONE;
1673 }
1674 
1675 /*----------------------------------------------------------------------------*/
1690 /*----------------------------------------------------------------------------*/
1691 
1692 cpl_table * gravi_metrology_create (cpl_table * metrology_table,
1693  cpl_propertylist * header)
1694 {
1695  gravi_msg_function_start(1);
1696  cpl_ensure (metrology_table, CPL_ERROR_NULL_INPUT, NULL);
1697  cpl_ensure (header, CPL_ERROR_NULL_INPUT, NULL);
1698 
1699  /* Read MJD of PRC */
1700  double mjd0 = gravi_convert_to_mjd (gravi_pfits_get_start_prcacq (header));
1701 
1702  /* Create the output table for VIS_MET */
1703  int ntel = 4;
1704  cpl_size nrow = cpl_table_get_nrow (metrology_table);
1705  cpl_table * vismet_table = cpl_table_new (nrow * ntel);
1706 
1707  /* Create the TIME column */
1708  cpl_table_new_column (vismet_table, "TIME", CPL_TYPE_INT);
1709  cpl_table_set_column_unit (vismet_table, "TIME", "usec");
1710 
1711  /* Create the MJD column */
1712  cpl_table_new_column (vismet_table, "MJD", CPL_TYPE_DOUBLE);
1713  cpl_table_set_column_unit (vismet_table, "MJD", "day");
1714 
1715  /* Fill the TIME and MJD column */
1716  for (cpl_size row = 0; row < nrow; row++) {
1717  int time_met = cpl_table_get_int (metrology_table, "TIME", row, NULL);
1718  double mjd_met = time_met / 86400.E6 + mjd0;
1719  for (cpl_size tel = 0; tel < ntel; tel++) {
1720  cpl_table_set_int (vismet_table, "TIME", row*ntel+tel, time_met);
1721  cpl_table_set_double (vismet_table, "MJD", row*ntel+tel, mjd_met);
1722  }
1723  }
1724 
1725  /* Return */
1726  gravi_msg_function_exit(1);
1727  return vismet_table;
1728 }
1729 
1730 /*----------------------------------------------------------------------------*/
1743 /*----------------------------------------------------------------------------*/
1744 
1745 cpl_error_code gravi_metrology_acq (cpl_table * visacq_table,
1746  cpl_table * vismet_table,
1747  double delay,
1748  cpl_propertylist * header)
1749 {
1750  gravi_msg_function_start(1);
1751  cpl_ensure_code (visacq_table, CPL_ERROR_NULL_INPUT);
1752  cpl_ensure_code (vismet_table, CPL_ERROR_NULL_INPUT);
1753  cpl_ensure_code (header, CPL_ERROR_NULL_INPUT);
1754 
1755  cpl_msg_info (cpl_func,"Use acq-correction-delay = %.3f [s]",delay);
1756 
1757  /* Get size */
1758  int ntel = 4;
1759  cpl_size nrow_met = cpl_table_get_nrow (vismet_table) / ntel;
1760  cpl_size nrow_acq = cpl_table_get_nrow (visacq_table) / ntel;
1761 
1762  /* Create a temporary table with the time shifted
1763  * by the delta in [s] */
1764  cpl_table * visacq_tmp = cpl_table_new (nrow_acq * ntel);
1765 
1766  cpl_table_duplicate_column (visacq_tmp, "TIME", visacq_table, "TIME");
1767  cpl_table_add_scalar (visacq_tmp, "TIME", 1e6 * delay);
1768 
1769  /* Copy necessary data */
1770  cpl_table_duplicate_column (visacq_tmp, "OPD_PUPIL",
1771  visacq_table, "OPD_PUPIL");
1772  cpl_table_duplicate_column (visacq_tmp, "PUPIL_NSPOT",
1773  visacq_table, "PUPIL_NSPOT");
1774 
1775  cpl_table_duplicate_column (visacq_tmp, "FIELD_FIBER_DX",
1776  visacq_table, "FIELD_FIBER_DX");
1777  cpl_table_duplicate_column (visacq_tmp, "FIELD_FIBER_DY",
1778  visacq_table, "FIELD_FIBER_DY");
1779 
1780  /* Get the ACQ DIT in [us] */
1781  double dit_acq = gravi_pfits_get_dit_acqcam (header) * 1e6;
1782  cpl_msg_info (cpl_func,"dit_acq = %g [us]", dit_acq);
1783 
1784  /* Create SYNC information */
1785  gravi_signal_create_sync (visacq_tmp, ntel, dit_acq,
1786  vismet_table, ntel, "MET");
1787 
1788  /* Create column in output table */
1789  gravi_table_new_column (vismet_table, "OPD_PUPIL", "m", CPL_TYPE_DOUBLE);
1790  double * opd_met = cpl_table_get_data_double (vismet_table, "OPD_PUPIL");
1791 
1792  gravi_table_new_column (vismet_table, "FIELD_FIBER_DX", "pix", CPL_TYPE_DOUBLE);
1793  double * fdx_met = cpl_table_get_data_double (vismet_table, "FIELD_FIBER_DX");
1794 
1795  gravi_table_new_column (vismet_table, "FIELD_FIBER_DY", "pix", CPL_TYPE_DOUBLE);
1796  double * fdy_met = cpl_table_get_data_double (vismet_table, "FIELD_FIBER_DY");
1797 
1798  /* Get data from input table */
1799  double * opd_acq = cpl_table_get_data_double (visacq_tmp, "OPD_PUPIL");
1800  double * fdx_acq = cpl_table_get_data_double (visacq_tmp, "FIELD_FIBER_DX");
1801  double * fdy_acq = cpl_table_get_data_double (visacq_tmp, "FIELD_FIBER_DY");
1802  int * nspot = cpl_table_get_data_int (visacq_tmp, "PUPIL_NSPOT");
1803  int * first = cpl_table_get_data_int (visacq_tmp, "FIRST_MET");
1804  int * last = cpl_table_get_data_int (visacq_tmp, "LAST_MET");
1805  CPLCHECK_MSG ("Cannot load data");
1806 
1807  /* Loop on beam */
1808  for (cpl_size tel = 0; tel < ntel; tel++) {
1809 
1810  /* Loop on ACQ rows with undetected spot */
1811  if (gravi_pfits_get_mjd (header) < 57876.5) {
1812  cpl_msg_info (cpl_func, "Compute OPD_PUPIL for blink ACQ frames");
1813  for (cpl_size row = 1; row < nrow_acq-1; row++) {
1814  if (nspot[row*ntel+tel] == 0 &&
1815  nspot[(row-1)*ntel+tel] > 0 &&
1816  nspot[(row+1)*ntel+tel] > 0) {
1817  opd_acq[row*ntel+tel] = 0.5* (opd_acq[(row-1)*ntel+tel] + opd_acq[(row+1)*ntel+tel]);
1818  }
1819  }
1820  }
1821 
1822  /* Loop on ACQ rows, fill the corresponding MET rows */
1823  for (cpl_size row = 0; row < nrow_acq; row++) {
1824  for (cpl_size row_met = first[row*ntel+tel]; row_met < last[row*ntel+tel]; row_met++) {
1825  opd_met[row_met*ntel+tel] = opd_acq[row*ntel+tel];
1826  fdx_met[row_met*ntel+tel] = fdx_acq[row*ntel+tel];
1827  fdy_met[row_met*ntel+tel] = fdy_acq[row*ntel+tel];
1828  }
1829  }
1830 
1831  /* Loop on MET rows, to fill the empty by the closet futur value */
1832  double opd = opd_met[nrow_acq*ntel+tel];
1833  double fdx = fdx_met[nrow_acq*ntel+tel];
1834  double fdy = fdy_met[nrow_acq*ntel+tel];
1835  for (cpl_size row = nrow_met-1; row >= 0; row--) {
1836  if (opd_met[row*ntel+tel] != 0 ) opd = opd_met[row*ntel+tel];
1837  else opd_met[row*ntel+tel] = opd;
1838  if (fdx_met[row*ntel+tel] != 0 ) fdx = fdx_met[row*ntel+tel];
1839  else fdx_met[row*ntel+tel] = fdx;
1840  if (fdy_met[row*ntel+tel] != 0 ) fdy = fdy_met[row*ntel+tel];
1841  else fdy_met[row*ntel+tel] = fdy;
1842  }
1843 
1844  /* Loop on MET rows, to fill the empty by the closet past value */
1845  opd = opd_met[0*ntel+tel];
1846  fdx = fdx_met[0*ntel+tel];
1847  fdy = fdy_met[0*ntel+tel];
1848  for (cpl_size row = 0; row < nrow_met; row++) {
1849  if (opd_met[row*ntel+tel] != 0) opd = opd_met[row*ntel+tel];
1850  else opd_met[row*ntel+tel] = opd;
1851  if (fdx_met[row*ntel+tel] != 0) fdx = fdx_met[row*ntel+tel];
1852  else fdx_met[row*ntel+tel] = fdx;
1853  if (fdy_met[row*ntel+tel] != 0) fdy = fdy_met[row*ntel+tel];
1854  else fdy_met[row*ntel+tel] = fdy;
1855  }
1856 
1857  }/* End loop on beam */
1858 
1859  /* Free the tmp table */
1860  FREE (cpl_table_delete, visacq_tmp);
1861 
1862  /* Return */
1863  gravi_msg_function_exit(1);
1864  return CPL_ERROR_NONE;
1865 }
1866 
1867 /*----------------------------------------------------------------------------*/
1881 /*----------------------------------------------------------------------------*/
1882 
1883 cpl_error_code gravi_metrology_drs (cpl_table * metrology_table,
1884  cpl_table * vismet_table,
1885  cpl_propertylist * header)
1886 {
1887  gravi_msg_function_start(1);
1888  cpl_ensure_code (metrology_table, CPL_ERROR_NULL_INPUT);
1889  cpl_ensure_code (header, CPL_ERROR_NULL_INPUT);
1890 
1891  int ntel = 4, ndiode = 4;
1892  char name[100];
1893 
1894  /* Parameters */
1895  int ind_sintel_FT[4][4]={{0,2,4,6},
1896  {8,10,12,14},
1897  {16,18,20,22},
1898  {24,26,28,30}};
1899  int ind_costel_FT[4][4]={{1,3,5,7},
1900  {9,11,13,15},
1901  {17,19,21,23},
1902  {25,27,29,31}};
1903  int ind_sintel_SC[4][4]={{32,34,36,38},
1904  {40,42,44,46},
1905  {48,50,52,54},
1906  {56,58,60,62}};
1907  int ind_costel_SC[4][4]={{33,35,37,39},
1908  {41,43,45,47},
1909  {49,51,53,55},
1910  {57,59,61,63}};
1911  int ind_sinfc_FT[4]={64,66,68,70};
1912  int ind_cosfc_FT[4]={65,67,69,71};
1913  int ind_sinfc_SC[4]={72,74,76,78};
1914  int ind_cosfc_SC[4]={73,75,77,79};
1915 
1916  cpl_array ** raw_met=cpl_table_get_data_array(metrology_table,"VOLT");
1917  CPLCHECK_MSG("get data met phase at tel");
1918 
1919 
1920  /*
1921  * Create the vismet_table
1922  */
1923 
1924  cpl_msg_info (cpl_func,"Fill the OI_VIS_MET table with the DRS algorithm");
1925 
1926  cpl_size nbrow_met = cpl_table_get_nrow (metrology_table);
1927 
1928  double phase_temp[4]={0,0,0,0}, phase_rtc;
1929  int k_wrap[4]={0,0,0,0};
1930 
1931  /* Create the output table for VIS_MET
1932  * PHASE_TEL and PHASE_FC are defined as FT-SC */
1933  cpl_table_new_column (vismet_table, "PHASE_FC", CPL_TYPE_DOUBLE);
1934  cpl_table_set_column_unit (vismet_table, "PHASE_FC", "rad");
1935  cpl_table_fill_column_window (vismet_table, "PHASE_FC", 0, nbrow_met * ntel, 0.0);
1936  cpl_table_new_column_array (vismet_table, "PHASE_TEL", CPL_TYPE_DOUBLE, ndiode);
1937  cpl_table_set_column_unit (vismet_table, "PHASE_TEL", "rad");
1938 
1939  const char * date = gravi_pfits_get_met_ph(header);
1940  const char * acq_date = gravi_pfits_get_start_prcacq(header);
1941  CPLCHECK_MSG ("Cannot get dates");
1942 
1943  double met_mjd = 86400*1e6*(gravi_convert_to_mjd(date) -
1944  gravi_convert_to_mjd(acq_date));
1945  CPLCHECK_MSG ("Cannot convert dates");
1946 
1947 
1948 
1949  /*
1950  * Compute the metrology phase at FC, without
1951  * any smoothing
1952  */
1953  double k_phase[4]={0,0,0,0};
1954 
1955  /* Loop on row of the metrology */
1956  int met_date_row = -1;
1957  for (cpl_size row = 0; row < nbrow_met; row ++) {
1958 
1959  /* Look for the reference row */
1960  int time_met = cpl_table_get_int (metrology_table, "TIME", row, NULL);
1961  if (row > 0) {
1962  if ((time_met > met_mjd) && (cpl_table_get_int (metrology_table, "TIME", row-1, NULL) < met_mjd)) {
1963  met_date_row = row;
1964  }
1965  }
1966 
1967  /* Loop on tel */
1968  for (int tel = 0; tel < ntel; tel++) {
1969 
1970  /* Compute the phase from the VOLTs */
1971  double phi_ft = atan2 (cpl_array_get (raw_met[row],ind_sinfc_FT[tel], NULL),
1972  cpl_array_get (raw_met[row],ind_cosfc_FT[tel], NULL));
1973 
1974  double phi_sc = atan2 (cpl_array_get (raw_met[row],ind_sinfc_SC[tel], NULL),
1975  cpl_array_get (raw_met[row],ind_cosfc_SC[tel], NULL));
1976 
1977  /* Catch errors */
1978  CPLCHECK_MSG("Error at computation of phase");
1979 
1980  /* unwrap (phi_ft - phi_sc) */
1981  if ((phi_ft - phi_sc) > M_PI) phi_ft-=2*M_PI;
1982  if ((phi_ft - phi_sc) < -M_PI) phi_ft+=2*M_PI;
1983 
1984  /* Keep memory of wraps */
1985  if ((phi_ft - phi_sc)-phase_temp[tel] > M_PI) k_wrap[tel]--;
1986  else if ((phi_ft - phi_sc)-phase_temp[tel] < -M_PI) k_wrap[tel]++;
1987 
1988  /* Integrate phase */
1989  phase_temp[tel] = (phi_ft - phi_sc);
1990  double phase_d = (phase_temp[tel] + 2 *k_wrap[tel]* M_PI) ;
1991 
1992  /* When we reach the metrology reference date */
1993  if (met_date_row == row){
1994  sprintf (name, "ESO OCS MET PH_FC%d_FT", tel+1);
1995  phase_rtc = cpl_propertylist_get_double (header, name);
1996  sprintf (name, "ESO OCS MET PH_FC%d_SC", tel+1);
1997  phase_rtc = phase_rtc - cpl_propertylist_get_double (header, name);
1998  k_phase[tel] = phase_rtc - (phase_temp[tel] + 2 *k_wrap[tel]* M_PI);
1999  k_phase[tel] = floor(k_phase[tel]/(2*M_PI)+0.5)*2*M_PI;
2000  }
2001 
2002  /* Set the PHASE_SC and the TIME */
2003  cpl_table_set (vismet_table, "PHASE_FC", row*ntel+tel, phase_d);
2004 
2005  CPLCHECK_MSG("Computing the metrology phase at FC");
2006  }
2007  /* End loop on tel */
2008  }
2009  /* End loop on rows */
2010 
2011  /* IF error of timing */
2012  if (met_date_row == -1){
2013  cpl_error_set_message (cpl_func, CPL_ERROR_ILLEGAL_INPUT,
2014  "The metrology phase date is not within"
2015  " the boundaries of RMN acquisition");
2016  return CPL_ERROR_ILLEGAL_INPUT;
2017  }
2018 
2019  /* Loop on rows to re-apply to k_phase */
2020  for (cpl_size row = 0; row < nbrow_met; row++) {
2021  for (int tel = 0; tel < 4 ; tel++) {
2022  double value = cpl_table_get (vismet_table, "PHASE_FC", row*ntel+tel, NULL);
2023  cpl_table_set (vismet_table, "PHASE_FC", row*ntel+tel, value + k_phase[tel]);
2024  }
2025  } /* End loop on rows */
2026 
2027  CPLCHECK_MSG("Computing the metrology phase at FC");
2028 
2029 
2030 
2031  /*
2032  * Compute the metrology phase at telescope, with a temporal
2033  * smoothing over n_filter samples
2034  */
2035 
2036  cpl_array * tel_phase = cpl_array_new (ndiode, CPL_TYPE_DOUBLE);
2037 
2038  int k_wrap_tel[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
2039  double phase_temp_tel[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
2040  double k_phase_tel[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
2041 
2042  cpl_array * phase;
2043  cpl_array * phase_ft = cpl_array_new (16, CPL_TYPE_DOUBLE_COMPLEX);
2044  cpl_array * phase_sc_conj = cpl_array_new (16, CPL_TYPE_DOUBLE_COMPLEX);
2045  CPLCHECK_MSG("creat array met phase at tel");
2046 
2047  cpl_array_fill_window_complex(phase_ft, 0, 16, 0.+I*0.);
2048  cpl_array_fill_window_complex(phase_sc_conj, 0, 16, 0.+I*0.);
2049  CPLCHECK_MSG("Fill win met phase at tel");
2050  int n_filter=25;
2051 
2052  /* Compute the sum phase_ft and phase_sc_conj of the first n_filter*2+1 row
2053  * phase_ft=<cos_ft>+i<sin_ft>
2054  * phase_sc_conj=<cos_sc>-i<sin_sc> */
2055  CPLCHECK_MSG("Init met phase at tel");
2056  for (cpl_size row=0; row<n_filter*2+1; row++) {
2057  for (int tel = 0; tel < ntel; tel++){
2058  for (int diode = 0; diode < ndiode; diode++){
2059  int ind = tel*ndiode+diode;
2060  cpl_array_set_complex(phase_ft, ind,
2061  cpl_array_get_complex(phase_ft,ind, NULL)
2062  +cpl_array_get(raw_met[row],ind_costel_FT[tel][diode], NULL)
2063  +I*cpl_array_get(raw_met[row],ind_sintel_FT[tel][diode], NULL));
2064  cpl_array_set_complex(phase_sc_conj,ind,
2065  cpl_array_get_complex(phase_sc_conj,ind, NULL)
2066  +cpl_array_get(raw_met[row],ind_costel_SC[tel][diode], NULL)
2067  -I*cpl_array_get(raw_met[row],ind_sintel_SC[tel][diode], NULL));
2068  }
2069  }
2070  }
2071  CPLCHECK_MSG("Computing met phase at tel");
2072 
2073  /* arg{FT - SC} */
2074  phase = cpl_array_duplicate (phase_ft);
2075  cpl_array_multiply (phase, phase_sc_conj);
2076  cpl_array_arg (phase);
2077 
2078  /* case for n_filter first lines */
2079  for (cpl_size row=0; row<n_filter+1; row++) {
2080  for (int tel = 0; tel < ntel; tel++) {
2081  for (int diode = 0; diode < ndiode; diode++) {
2082  cpl_array_set (tel_phase, diode, cpl_array_get (phase,tel*ndiode+diode, NULL));
2083  }
2084  cpl_table_set_array (vismet_table, "PHASE_TEL", row*ntel+tel, tel_phase);
2085  CPLCHECK_MSG ("Cannot set");
2086  }
2087  }
2088 
2089  /* case for all lines */
2090  for (cpl_size row=n_filter+1; row<nbrow_met-n_filter; row++) {
2091 
2092  /* update the mean phase_ft=<cos_ft>+i<sin_ft> and phase_sc_conj=<cos_sc>-i<sin_sc>
2093  * Remove element row-n_filter-1 and add row+n_filter*/
2094  for (int tel = 0; tel < ntel; tel++){
2095  for (int diode = 0; diode < ndiode; diode++){
2096  int ind = tel*ndiode+diode;
2097  cpl_array_set_complex(phase_ft, ind,
2098  cpl_array_get_complex(phase_ft,ind, NULL)
2099  -(cpl_array_get(raw_met[row-n_filter-1],ind_costel_FT[tel][diode], NULL)
2100  +I*cpl_array_get(raw_met[row-n_filter-1],ind_sintel_FT[tel][diode], NULL))
2101  +cpl_array_get(raw_met[row+n_filter],ind_costel_FT[tel][diode], NULL)
2102  +I*cpl_array_get(raw_met[row+n_filter],ind_sintel_FT[tel][diode], NULL));
2103  cpl_array_set_complex(phase_sc_conj,ind,
2104  cpl_array_get_complex(phase_sc_conj,ind, NULL)
2105  -(cpl_array_get(raw_met[row-n_filter-1],ind_costel_SC[tel][diode], NULL)
2106  -I*cpl_array_get(raw_met[row-n_filter-1],ind_sintel_SC[tel][diode], NULL))
2107  +cpl_array_get(raw_met[row+n_filter],ind_costel_SC[tel][diode], NULL)
2108  -I*cpl_array_get(raw_met[row+n_filter],ind_sintel_SC[tel][diode], NULL));
2109  CPLCHECK_MSG("loop met phase at tel");
2110  }
2111  }
2112 
2113  cpl_array_delete (phase);
2114 
2115  /* compute the phase */
2116  CPLCHECK_MSG("Computing met phase at tel");
2117  phase=cpl_array_duplicate(phase_ft);
2118  cpl_array_multiply(phase, phase_sc_conj);
2119  cpl_array_arg(phase);
2120 
2121  /* Keep memory of wraps */
2122  for (int ind=0; ind<16; ind++){
2123  if (cpl_array_get(phase, ind, NULL)-phase_temp_tel[ind] > M_PI) k_wrap_tel[ind]--;
2124  else if (cpl_array_get(phase, ind, NULL)-phase_temp_tel[ind] < -M_PI) k_wrap_tel[ind]++;
2125 
2126  phase_temp_tel[ind]=cpl_array_get(phase, ind, NULL);
2127  cpl_array_set(phase, ind,(phase_temp_tel[ind] + 2 *k_wrap_tel[ind]* M_PI)) ;
2128  }
2129  CPLCHECK_MSG("Unwrap met phase at tel");
2130 
2131  /* When we reach the metrology reference date */
2132  if (met_date_row == row){
2133  for (int tel = 0; tel < ntel; tel++){
2134  for (int diode = 0; diode < 4; diode++){
2135  int ind = tel*ndiode+diode;
2136  sprintf (name, "ESO OCS MET PH_T%d_D%d_FT",tel+1, diode+1);
2137  phase_rtc = cpl_propertylist_get_double (header, name);
2138  sprintf (name, "ESO OCS MET PH_T%d_D%d_SC",tel+1, diode+1);
2139  phase_rtc = phase_rtc - cpl_propertylist_get_double (header, name);
2140  k_phase_tel[ind] = phase_rtc - (phase_temp_tel[ind] + 2 *k_wrap_tel[ind]* M_PI);
2141  k_phase_tel[ind] = floor(k_phase_tel[ind]/(2*M_PI)+0.5)*2*M_PI;
2142  }
2143  }
2144  CPLCHECK_MSG("Unwrap met phase at tel");
2145  }
2146 
2147  /* Descramble and set */
2148  for (int tel = 0; tel < ntel; tel++) {
2149  for (int diode = 0; diode < ndiode; diode++) {
2150  cpl_array_set (tel_phase, diode, cpl_array_get (phase,tel*ndiode+diode, NULL));
2151  }
2152  cpl_table_set_array (vismet_table, "PHASE_TEL", row*ntel+tel, tel_phase);
2153  CPLCHECK_MSG ("Cannot set");
2154  }
2155 
2156  } /* End loop on rows */
2157 
2158  cpl_array_delete (phase_sc_conj);
2159  cpl_array_delete (phase);
2160  cpl_array_delete (phase_ft);
2161  cpl_array_delete (tel_phase);
2162 
2163  /* case for n_filter last lines */
2164  for (int tel = 0; tel < ntel; tel++) {
2165  const cpl_array * last_phase = cpl_table_get_array (vismet_table,"PHASE_TEL", (nbrow_met-n_filter-1)*ntel+tel);
2166  for (cpl_size row = nbrow_met-n_filter; row<nbrow_met; row++) {
2167  cpl_table_set_array (vismet_table,"PHASE_TEL", row*ntel+tel, last_phase);
2168  CPLCHECK_MSG ("Cannot set");
2169  }
2170  }
2171 
2172  /* IF error of timing */
2173  if (met_date_row == -1){
2174  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
2175  "The metrology phase date is not within"
2176  " the boundaries of RMN acquisition");
2177  return CPL_ERROR_ILLEGAL_INPUT;
2178  }
2179 
2180  /* Loop on rows to re-apply to k_phase */
2181  cpl_array ** all_phase = cpl_table_get_data_array (vismet_table, "PHASE_TEL");
2182 
2183  for (cpl_size row = 0; row < nbrow_met; row++) {
2184  for (int tel = 0; tel < ntel; tel++) {
2185  for (int diode = 0; diode < 4; diode++) {
2186  int ind = tel*ndiode+diode;
2187  double value = cpl_array_get (all_phase[row*ntel+tel], diode, NULL);
2188  cpl_array_set (all_phase[row*ntel+tel], diode, value + k_phase_tel[ind]);
2189  }
2190  }
2191  }
2192  /* End loop on rows */
2193 
2194 
2195  /*
2196  * Compute the TEL-FC complex phasor = Vtel * conj (Vfc)
2197  * without any smoothing
2198  */
2199  cpl_msg_info (cpl_func,"Compute PHASOR_TELFC");
2200 
2201  cpl_table_new_column_array (vismet_table, "PHASOR_TELFC", CPL_TYPE_DOUBLE_COMPLEX, ndiode);
2202  cpl_table_set_column_unit (vismet_table, "PHASOR_TELFC", "V^4");
2203  cpl_array ** phasor_telfc = cpl_table_get_data_array (vismet_table, "PHASOR_TELFC");
2204 
2205  for (cpl_size row = 0; row < nbrow_met; row++) {
2206  for (int tel = 0; tel < ntel; tel++) {
2207  phasor_telfc[row*ntel+tel] = cpl_array_new (ndiode, CPL_TYPE_DOUBLE_COMPLEX);
2208 
2209  double complex V_fc;
2210  V_fc = (cpl_array_get (raw_met[row],ind_cosfc_FT[tel], NULL) +
2211  cpl_array_get (raw_met[row],ind_sinfc_FT[tel], NULL) * I) *
2212  (cpl_array_get (raw_met[row],ind_cosfc_SC[tel], NULL) -
2213  cpl_array_get (raw_met[row],ind_sinfc_SC[tel], NULL) * I);
2214  CPLCHECK_MSG ("Cannot compute V_fc");
2215 
2216  for (int diode = 0; diode < 4; diode++) {
2217  double complex V_tel;
2218  V_tel = (cpl_array_get (raw_met[row],ind_costel_FT[tel][diode], NULL) +
2219  cpl_array_get (raw_met[row],ind_sintel_FT[tel][diode], NULL) * I) *
2220  (cpl_array_get (raw_met[row],ind_costel_SC[tel][diode], NULL) -
2221  cpl_array_get (raw_met[row],ind_sintel_SC[tel][diode], NULL) * I);
2222  cpl_array_set_complex (phasor_telfc[row*ntel+tel], diode,
2223  V_tel * conj (V_fc));
2224  CPLCHECK_MSG ("Cannot set V_tel * conj (V_fc)");
2225  } /* End loop on diode */
2226 
2227  } /* End loop on tel */
2228  } /* End loop on rows */
2229 
2230  gravi_msg_function_exit(1);
2231  return CPL_ERROR_NONE;
2232 }
2233 
2234 
2235 
2236 /*----------------------------------------------------------------------------*/
2248 /*----------------------------------------------------------------------------*/
2249 cpl_error_code gravi_metrology_tac (cpl_table * metrology_table,
2250  cpl_table * vismet_table,
2251  cpl_propertylist * header)
2252 {
2253  gravi_msg_function_start(1);
2254  cpl_ensure_code (metrology_table, CPL_ERROR_NULL_INPUT);
2255  cpl_ensure_code (header, CPL_ERROR_NULL_INPUT);
2256  cpl_ensure_code (vismet_table, CPL_ERROR_NULL_INPUT);
2257 
2258  char card_ft[100];
2259  char card_sc[100];
2260  int ndiode = 4, ntel = 4;
2261  cpl_size nrow_met = cpl_table_get_nrow (metrology_table);
2262 
2263  /*
2264  * Copy data to ensure they are of type DOUBLE
2265  * volts[nrow][ndiode]
2266  */
2267 
2268  double** volts = cpl_malloc (nrow_met * sizeof(double*));
2269  cpl_array ** volts_array = cpl_table_get_data_array (metrology_table,"VOLT");
2270  for (cpl_size row = 0; row < nrow_met; row ++) {
2271  volts[row] = cpl_malloc (80 * sizeof(double));
2272  for (cpl_size diode = 0; diode < 80; diode ++) {
2273  volts[row][diode] = cpl_array_get (volts_array[row], diode, NULL);
2274  }
2275  }
2276 
2277  CPLCHECK_MSG ("Cannot load metrology data");
2278 
2279  /*
2280  * Allocate memory for FC the output
2281  * opd_fc[nrow*ntel]
2282  */
2283 
2284  gravi_table_new_column (vismet_table, "FLAG_FC", NULL, CPL_TYPE_INT);
2285  int * flag_fc = cpl_table_get_data_int (vismet_table, "FLAG_FC");
2286 
2287  gravi_table_new_column (vismet_table, "OPD_FC", "m", CPL_TYPE_DOUBLE);
2288  double * opd_fc = cpl_table_get_data_double (vismet_table, "OPD_FC");
2289 
2290  gravi_table_new_column (vismet_table, "VAMP_FC_FT", "V", CPL_TYPE_DOUBLE);
2291  double * coher_fc_ft = cpl_table_get_data_double (vismet_table, "VAMP_FC_FT");
2292 
2293  gravi_table_new_column (vismet_table, "VAMP_FC_SC", "V", CPL_TYPE_DOUBLE);
2294  double * coher_fc_sc = cpl_table_get_data_double (vismet_table, "VAMP_FC_SC");
2295 
2296  /*
2297  * Allocate memory for TEL output
2298  * opd_tel[nrow*ntel][ndiode]
2299  */
2300 
2301  gravi_table_new_column_array (vismet_table, "FLAG_TEL", NULL, CPL_TYPE_INT, ndiode);
2302  cpl_array ** flag_tel_array = cpl_table_get_data_array (vismet_table,"FLAG_TEL");
2303 
2304  gravi_table_new_column_array (vismet_table, "OPD_TEL", "m", CPL_TYPE_DOUBLE, ndiode);
2305  cpl_array ** opd_tel_array = cpl_table_get_data_array (vismet_table,"OPD_TEL");
2306 
2307  gravi_table_new_column_array (vismet_table, "VAMP_TEL_FT", "V", CPL_TYPE_DOUBLE, ndiode);
2308  cpl_array ** coher_tel_ft_array = cpl_table_get_data_array (vismet_table,"VAMP_TEL_FT");
2309 
2310  gravi_table_new_column_array (vismet_table, "VAMP_TEL_SC", "V", CPL_TYPE_DOUBLE, ndiode);
2311  cpl_array ** coher_tel_sc_array = cpl_table_get_data_array (vismet_table,"VAMP_TEL_SC");
2312 
2313 
2314  /* Wrap the newly computed phi_tel into the cpl_table. Wrapping make
2315  * the data 'valid' without having to copy them. Thus efficient */
2316  double ** opd_tel = cpl_malloc (sizeof(double*) * nrow_met * ntel);
2317  int ** flag_tel = cpl_malloc (sizeof(int*) * nrow_met * ntel);
2318  double ** coher_tel_ft = cpl_malloc (sizeof(double*) * nrow_met * ntel);
2319  double ** coher_tel_sc = cpl_malloc (sizeof(double*) * nrow_met * ntel);
2320 
2321  for (cpl_size row = 0; row < nrow_met * ntel; row ++) {
2322  flag_tel[row] = cpl_malloc (sizeof(int) * ndiode);
2323  flag_tel_array[row] = cpl_array_wrap_int (flag_tel[row], ndiode);
2324 
2325  opd_tel[row] = cpl_malloc (sizeof(double) * ndiode);
2326  opd_tel_array[row] = cpl_array_wrap_double (opd_tel[row], ndiode);
2327 
2328  coher_tel_ft[row] = cpl_malloc (sizeof(double) * ndiode);
2329  coher_tel_ft_array[row] = cpl_array_wrap_double (coher_tel_ft[row], ndiode);
2330 
2331  coher_tel_sc[row] = cpl_malloc (sizeof(double) * ndiode);
2332  coher_tel_sc_array[row] = cpl_array_wrap_double (coher_tel_sc[row], ndiode);
2333  }
2334 
2335  CPLCHECK_MSG ("Cannot allocate output memory");
2336 
2337 
2338 
2339  /* Init the TAC algorithm -- this allocate memory */
2340  structTacData * tacData;
2341  structTacConfiguration * tacConfiguration;
2342 
2343  /* get the laser wavelength data */
2344  double lambda_met_mean = gravi_pfits_get_met_wavelength_mean(header, metrology_table);
2345  cpl_msg_info (cpl_func,"Lambda met mean :%f nm", lambda_met_mean*1e9);
2346 
2347  tacData = metrology_makeDefaultTacData(lambda_met_mean);
2348  tacConfiguration = tacData->tacConfiguration;
2349 
2350  /* Loop on time sample to run the TAC algorithm */
2351  for(long sample_number = 1; sample_number <= nrow_met; sample_number++) {
2352 
2353  /* Run the TAC algorithm */
2354  tacData->sample_number = sample_number;
2355  tacData->buffer_idx_avg = ((sample_number - 1) % tacConfiguration->number_to_average);
2356  metrology_read_voltages(tacData, volts[sample_number-1]);
2357  metrology_algorithm(tacData);
2358 
2359  /* Fill the output arrays as D1T1, D2T1, D3T1, D4T1, D1T2, D2T2... */
2360  int idx_ft = 0;
2361  int idx_sc = 32;
2362  for (int idx = 0, tel = 0; tel < ntel; tel++) {
2363  cpl_size nmet = (sample_number-1)*ntel + tel;
2364 
2365  for (int diode = 0; diode < ndiode; diode++) {
2366  /* TAC computation */
2367  opd_tel[nmet][diode] = tacData->opl_telescope_diode[tel][diode];
2368  flag_tel[nmet][diode] = tacData->total_flag_telescope[tel][diode][FT] | tacData->total_flag_telescope[tel][diode][SC];
2369 
2370  /* Volt amplitude */
2371  coher_tel_ft[nmet][diode] = sqrt (volts[sample_number-1][idx_ft+idx*2] * volts[sample_number-1][idx_ft+idx*2] +
2372  volts[sample_number-1][idx_ft+idx*2+1] * volts[sample_number-1][idx_ft+idx*2+1]);
2373  coher_tel_sc[nmet][diode] = sqrt (volts[sample_number-1][idx_sc+idx*2] * volts[sample_number-1][idx_sc+idx*2] +
2374  volts[sample_number-1][idx_sc+idx*2+1] * volts[sample_number-1][idx_sc+idx*2+1]);
2375  idx++;
2376  }
2377  }
2378 
2379  /* Fill the output arrays as T1, T2, T3, T4... */
2380  idx_ft = 64;
2381  idx_sc = 72;
2382  for (int idx = 0, tel = 0; tel < ntel; tel++) {
2383  cpl_size nmet = (sample_number-1)*ntel + tel;
2384 
2385  /* TAC computation */
2386  opd_fc[nmet] = tacData->opl_fiber_coupler[tel];
2387  flag_fc[nmet] = tacData->total_flag_fiber_coupler[tel][FT] | tacData->total_flag_fiber_coupler[tel][SC];
2388  /* Volt amplitude */
2389  coher_fc_ft[nmet] = sqrt (volts[sample_number-1][idx_ft+idx*2] * volts[sample_number-1][idx_ft+idx*2] +
2390  volts[sample_number-1][idx_ft+idx*2+1] * volts[sample_number-1][idx_ft+idx*2+1]);
2391  coher_fc_sc[nmet] = sqrt (volts[sample_number-1][idx_sc+idx*2] * volts[sample_number-1][idx_sc+idx*2] +
2392  volts[sample_number-1][idx_sc+idx*2+1] * volts[sample_number-1][idx_sc+idx*2+1]);
2393  idx++;
2394  }
2395 
2396  }
2397  /* End loop on time sample */
2398 
2399 
2400  /* Get the TIME of the header reference phase */
2401  const char * date = gravi_pfits_get_met_ph (header);
2402  const char * acq_date = gravi_pfits_get_start_prcacq (header);
2403  double time_ref = 86400*1e6*(gravi_convert_to_mjd (date) - gravi_convert_to_mjd (acq_date));
2404  int * time_met = cpl_table_get_data_int (metrology_table, "TIME");
2405 
2406  cpl_size row_ref = -1;
2407  for (cpl_size row = 1; row < nrow_met; row++)
2408  if (time_met[row]>time_ref && time_met[row-1]<time_ref) {
2409  row_ref = row; break;
2410  }
2411 
2412  /* Check if we have found the metrology reference date inside the file */
2413  if (row_ref == -1) {
2414  cpl_error_set_message(cpl_func, CPL_ERROR_ILLEGAL_INPUT,
2415  "The metrology phase date is not within"
2416  " the boundaries of RMN acquisition");
2417  return CPL_ERROR_NULL_INPUT;
2418  } else {
2419  cpl_msg_info (cpl_func,"The metrology phase date is OK (row %lld over %lld).", row_ref, nrow_met);
2420  }
2421 
2422  /* Re-install the absolute phase for the FC diodes */
2423  for (int tel = 0; tel < ntel; tel++) {
2424  sprintf (card_ft, "ESO OCS MET PH_FC%d_FT", tel+1);
2425  sprintf (card_sc, "ESO OCS MET PH_FC%d_SC", tel+1);
2426  double opd_ref = lambda_met_mean / CPL_MATH_2PI *
2427  (cpl_propertylist_get_double (header, card_sc) - cpl_propertylist_get_double (header, card_ft)) -
2428  opd_fc[row_ref*ntel+tel];
2429  double opd_ref_int = gravi_round (opd_ref / lambda_met_mean) * lambda_met_mean;
2430  for (cpl_size row = 0; row < nrow_met; row++) {
2431  opd_fc[row*ntel+tel] += opd_ref_int;
2432  }
2433  }
2434  /* End loop on tel and diodes */
2435 
2436  /* Re-install the absolute phase for the TEL diodes */
2437  for (int tel = 0; tel < ntel; tel++) {
2438  for (int diode = 0; diode < ndiode; diode++) {
2439  sprintf (card_ft,"ESO OCS MET PH_T%d_D%d_FT", tel+1, diode+1);
2440  sprintf (card_sc,"ESO OCS MET PH_T%d_D%d_SC", tel+1, diode+1);
2441  double opd_ref = lambda_met_mean / CPL_MATH_2PI *
2442  (cpl_propertylist_get_double (header, card_sc) - cpl_propertylist_get_double (header, card_ft)) -
2443  opd_tel[row_ref*ntel+tel][diode];
2444  double opd_ref_int = gravi_round (opd_ref / lambda_met_mean) * lambda_met_mean;
2445  for (cpl_size row = 0; row < nrow_met; row++) {
2446  opd_tel[row*ntel+tel][diode] += opd_ref_int;
2447  }
2448  }
2449  }
2450 
2451  CPLCHECK_MSG ("Cannot fill result of metrology computation");
2452 
2453  /* Free the pointer to pointer to data */
2454  FREELOOP (cpl_free, volts, nrow_met);
2455  FREE (cpl_free, opd_tel);
2456  FREE (cpl_free, flag_tel);
2457  FREE (cpl_free, coher_tel_ft);
2458  FREE (cpl_free, coher_tel_sc);
2459 
2460  /* Free the TAC data */
2461  FREE (cpl_free, tacConfiguration);
2462  FREE (cpl_free, tacData);
2463 
2464  gravi_msg_function_exit(1);
2465  return CPL_ERROR_NONE;
2466 }
2467 
2468 
2469 
2470 /*----------------------------------------------------------------------------*/
2482 /*----------------------------------------------------------------------------*/
2483 cpl_error_code gravi_metrology_telfc (cpl_table * metrology_table,
2484  cpl_table * vismet_table,
2485  gravi_data * default_focus_data,
2486  cpl_propertylist * header)
2487 {
2488  gravi_msg_function_start(1);
2489 
2490  /* get size of arrays */
2491  cpl_size ndiode = 4, ntel = 4;
2492  cpl_size nrow_met = cpl_table_get_nrow (metrology_table);
2493 
2494  /* get OPD_FC and OPD_TEL */
2495  double * opd_fc = cpl_table_get_data_double (vismet_table, "OPD_FC");
2496  double ** opd_tel = gravi_table_get_data_array_double (vismet_table, "OPD_TEL");
2497 
2498  /* get the laser wavelength data */
2499  double lambda_met_mean = gravi_pfits_get_met_wavelength_mean(header, metrology_table);
2500  cpl_msg_info (cpl_func,"Lambda met mean :%f nm", lambda_met_mean*1e9);
2501 
2502 
2503  /************************************************/
2504  /* PART I: OPD_FC_CORR */
2505  /************************************************/
2506  /* Correction to Fiber Coupler Metrology OPD */
2507  /* from pupil motion, fiber pickup, and defocus */
2508  /************************************************/
2509 
2510 
2511  cpl_msg_info (cpl_func,"FE: calculate OPD_FC_CORR from OPD_PUPIL and pickup/defocus.");
2512 
2513  /* Create array in OI_VIS_MET table, fill with zeros, and get pointer */
2514  gravi_table_new_column (vismet_table, "OPD_FC_CORR", "m", CPL_TYPE_DOUBLE);
2515  double * opd_fc_corr = cpl_table_get_data_double (vismet_table, "OPD_FC_CORR");
2516 
2517  /* Pupil motion correction already calculated before and available from OPD_PUPIL */
2518  double * opd_pupil = NULL;
2519  if ( cpl_table_has_column(vismet_table, "OPD_PUPIL") ) {
2520  opd_pupil = cpl_table_get_data_double (vismet_table, "OPD_PUPIL");
2521  }
2522  else {
2523  cpl_msg_warning(cpl_func,"Cannot get the OPD_PUPIL (not computed) so will"
2524  "not correct for pupil opd (check the --reduce-acq-cam option)");
2525  }
2526 
2527  /* Retrieve object separation */
2528  double dx_in = gravi_pfits_get_sobj_x (header);
2529  double dy_in = gravi_pfits_get_sobj_y (header);
2530  double rho_in = sqrt(dx_in*dx_in + dy_in*dy_in);
2531  CPLCHECK_MSG ("Cannot get separation");
2532  cpl_msg_info (cpl_func,"FE: SOBJX, SOBJY in mas: %g, %g ", dx_in, dy_in );
2533 
2534  /* Force separation to zero in SINGLE */
2535  if (gravi_pfits_get_mode (header) == MODE_SINGLE) {
2536  cpl_msg_info (cpl_func,"Mode SINGLE thus separation forced to 0.0");
2537  rho_in = 0.;
2538  }
2539  cpl_msg_info (cpl_func,"FE: separation in mas: %g ", rho_in );
2540 
2541  /* Apply pupil, focus and pickup offsets */
2542  for (int tel = 0; tel < ntel; tel++) {
2543 
2544  double opd_fc_focus = gravi_metrology_get_fc_focus (header, tel, default_focus_data); // [m]
2545  double opd_fc_shift = gravi_metrology_get_fc_shift (header, tel, default_focus_data) * (rho_in/1000.0); // [m/arcsec] x [arcsec] = [m]
2546  CPLCHECK_INT("Cannot get Static calib");
2547 
2548  cpl_msg_info (cpl_func, "FE: Tel %d Pupil %g Focus %g Shift %g",
2549  tel,
2550  (opd_pupil?opd_pupil[0*ntel+tel]:0)*1e9,
2551  opd_fc_focus*1e9,
2552  opd_fc_shift*1e9 );
2553  for (cpl_size row = 0; row < nrow_met; row++) {
2554  opd_fc_corr[row*ntel+tel] = (opd_pupil?opd_pupil[row*ntel+tel]:0)
2555  + opd_fc_focus
2556  + opd_fc_shift;
2557  }
2558  }
2559 
2560 
2561  /*****************************************************************/
2562  /* PART II: OPD_TEL_CORR */
2563  /*****************************************************************/
2564  /* Deprojection for telescope diodes to center of telescope, */
2565  /* based on prior knowledge of object separation, */
2566  /* acqcam pointing offset, and astigmatism. */
2567  /*****************************************************************/
2568 
2569 
2570  cpl_msg_info (cpl_func,"FE: deproject telescope diodes to center of telescope in OPD_TEL_CORR.");
2571 
2572  /* Create array in OI_VIS_MET table, fill with zeros, and get list of pointer.
2573  * Note that this list has to be free */
2574  gravi_table_init_column_array (vismet_table, "OPD_TEL_CORR", "m", CPL_TYPE_DOUBLE, ndiode);
2575  double ** opd_tel_corr = gravi_table_get_data_array_double (vismet_table, "OPD_TEL_CORR");
2576  CPLCHECK_MSG ("Cannot init the OPD_TEL_CORR column");
2577 
2578  /* Read metrology receiver positions from MET_POS,
2579  * rec_az = [HIERARCH ESO MET AT<tel> REC<diode>X]
2580  * rec_zd = [HIERARCH ESO MET AT<tel> REC<diode>Y]
2581  * 1st index = beam/tel, 2nd index = diode */
2582  double rec_az[4][4];
2583  double rec_zd[4][4];
2584  for (int tel=0;tel<4;tel++) {
2585  for (int diode=0;diode<4;diode++) {
2586  rec_az[tel][diode] = gravi_metrology_get_posx (header, 3-tel, diode); /* in [m] in order GV 1,2,3,4 */
2587  rec_zd[tel][diode] = gravi_metrology_get_posy (header, 3-tel, diode); /* in [m] in order GV 1,2,3,4 */
2588  }
2589  }
2590 
2591  /*----- PART II.a: Separation deprojection with acqcam correction -----*/
2592 
2593  /* (rec_az E_AZ + rec_zd E_ZD) . (sobj_U E_U + sobj_V E_V) */
2594 
2595  /* Declare some variables */
2596  double deproject, northangle, field_dU, field_dV;
2597  char card[100];
2598 
2599  /* Vectors used in Julien's formula */
2600  cpl_vector * rec = cpl_vector_new (3);
2601  cpl_vector * sobj = cpl_vector_new (3);
2602 
2603  /* read E_U,V,AZ,ZD from OI_VIS_MET table */
2604  cpl_array ** E_U = cpl_table_get_data_array (vismet_table,"E_U");
2605  cpl_array ** E_V = cpl_table_get_data_array (vismet_table,"E_V");
2606  cpl_array ** E_AZ = cpl_table_get_data_array (vismet_table,"E_AZ");
2607  cpl_array ** E_ZD = cpl_table_get_data_array (vismet_table,"E_ZD");
2608 
2609  /* read the field fiber offset */
2610  double * field_dX = NULL;
2611  double * field_dY = NULL;
2612  if ( cpl_table_has_column (vismet_table, "FIELD_FIBER_DX")
2613  && cpl_table_has_column (vismet_table, "FIELD_FIBER_DX") ) {
2614  field_dX = cpl_table_get_data_double (vismet_table, "FIELD_FIBER_DX");
2615  field_dY = cpl_table_get_data_double (vismet_table, "FIELD_FIBER_DY");
2616  }
2617 
2618  /* some debug messages */
2619  cpl_msg_info (cpl_func,"FE: E_U = [%g, %g, %g].",
2620  cpl_array_get (E_U[0*ntel+0], 0, NULL),
2621  cpl_array_get (E_U[0*ntel+0], 1, NULL),
2622  cpl_array_get (E_U[0*ntel+0], 2, NULL));
2623  cpl_msg_info (cpl_func,"FE: E_V = [%g, %g, %g].",
2624  cpl_array_get (E_V[0*ntel+0], 0, NULL),
2625  cpl_array_get (E_V[0*ntel+0], 1, NULL),
2626  cpl_array_get (E_V[0*ntel+0], 2, NULL));
2627  cpl_msg_info (cpl_func,"FE: E_AZ = [%g, %g, %g].",
2628  cpl_array_get (E_AZ[0*ntel+0], 0, NULL),
2629  cpl_array_get (E_AZ[0*ntel+0], 1, NULL),
2630  cpl_array_get (E_AZ[0*ntel+0], 2, NULL));
2631  cpl_msg_info (cpl_func,"FE: E_ZD = [%g, %g, %g].",
2632  cpl_array_get (E_ZD[0*ntel+0], 0, NULL),
2633  cpl_array_get (E_ZD[0*ntel+0], 1, NULL),
2634  cpl_array_get (E_ZD[0*ntel+0], 2, NULL));
2635 
2636  /* loop over all column and diodes */
2637  for (int tel = 0; tel < ntel; tel++) {
2638 
2639  /* compute the north angle on acqcam [deg] */
2640  northangle = gravi_pfits_get_fangle_acqcam (header, tel);
2641 
2642  /* If available, get average image scale on acqcam [mas/pix] */
2643  double scale = 0.0;
2644  sprintf (card,"ESO QC ACQ FIELD%d SCALE", tel+1);
2645  if (cpl_propertylist_has (header, card))
2646  scale = cpl_propertylist_get_double (header, card);
2647 
2648  for (cpl_size row = 0; row < nrow_met; row++) {
2649 
2650  /* If available, transform field fiber offset from (x,y) acqcam [pix] to (U,V) sky [mas] */
2651  if (field_dX && field_dY) {
2652  field_dU = (field_dX[row*ntel+tel] * sin( (northangle+90.) * CPL_MATH_RAD_DEG )
2653  +field_dY[row*ntel+tel] * cos( (northangle+90.) * CPL_MATH_RAD_DEG ))*scale;
2654  field_dV = (field_dX[row*ntel+tel] * sin( (northangle ) * CPL_MATH_RAD_DEG )
2655  +field_dY[row*ntel+tel] * cos( (northangle ) * CPL_MATH_RAD_DEG ))*scale;
2656  } else {
2657  field_dU = 0.0;
2658  field_dV = 0.0;
2659  }
2660 
2661  for (int diode = 0; diode < ndiode; diode++) {
2662 
2663  /* Filling vectors of Julien's formula */
2664  cpl_vector_set (rec, 0, rec_az[tel][diode] * cpl_array_get (E_AZ[row*ntel+tel], 0, NULL)
2665  +rec_zd[tel][diode] * cpl_array_get (E_ZD[row*ntel+tel], 0, NULL));
2666  cpl_vector_set (rec, 1, rec_az[tel][diode] * cpl_array_get (E_AZ[row*ntel+tel], 1, NULL)
2667  +rec_zd[tel][diode] * cpl_array_get (E_ZD[row*ntel+tel], 1, NULL));
2668  cpl_vector_set (rec, 2, rec_az[tel][diode] * cpl_array_get (E_AZ[row*ntel+tel], 2, NULL)
2669  +rec_zd[tel][diode] * cpl_array_get (E_ZD[row*ntel+tel], 2, NULL));
2670  cpl_vector_set (sobj, 0, (dx_in-field_dU) * cpl_array_get (E_U[row*ntel+tel], 0, NULL)
2671  +(dy_in-field_dV) * cpl_array_get (E_V[row*ntel+tel], 0, NULL));
2672  cpl_vector_set (sobj, 1, (dx_in-field_dU) * cpl_array_get (E_U[row*ntel+tel], 1, NULL)
2673  +(dy_in-field_dV) * cpl_array_get (E_V[row*ntel+tel], 1, NULL));
2674  cpl_vector_set (sobj, 2, (dx_in-field_dU) * cpl_array_get (E_U[row*ntel+tel], 2, NULL)
2675  +(dy_in-field_dV) * cpl_array_get (E_V[row*ntel+tel], 2, NULL));
2676 
2677  /* calculate deprojection */
2678  deproject = cpl_vector_product(rec, sobj); /* in m * mas */
2679  deproject = deproject / 1000. / 3600. / 360. * TWOPI; /* convert in meter */
2680 
2681  /* some debug messages */
2682  if (row == 0 && tel == 0 && diode == 0) {
2683  cpl_msg_info (cpl_func,"FE: Julien deproject diode 0 in nm: %g", deproject*1e9);
2684  }
2685  if (row == 0 && tel == 0 && diode == 1) {
2686  cpl_msg_info (cpl_func,"FE: Julien deproject diode 1 in nm: %g", deproject*1e9);
2687  }
2688  if (row == 0 && tel == 0 && diode == 2) {
2689  cpl_msg_info (cpl_func,"FE: Julien deproject diode 2 in nm: %g", deproject*1e9);
2690  }
2691  if (row == 0 && tel == 0 && diode == 3) {
2692  cpl_msg_info (cpl_func,"FE: Julien deproject diode 3 in nm: %g", deproject*1e9);
2693  }
2694 
2695  /* store deprojection in opd_tel_corr */
2696  opd_tel_corr[row*ntel+tel][diode] = deproject;
2697  }
2698  }
2699  }
2700 
2701  /* Free memory */
2702  FREE (cpl_vector_delete, rec);
2703  FREE (cpl_vector_delete, sobj);
2704 
2705  /*----- Part II.b: Astigmatism -----*/
2706 
2707  /* local variables */
2708  double AstigmAmplitude; // [nm]
2709  double AstigmTheta; // [rad]
2710  double AstigmRadius; // [m]
2711  double astigm;
2712  double diodeang;
2713  double astang;
2714  double astradius;
2715 
2716  /* Paralactic angle is averaged from fitsheader */
2717  // TODO: This needs to be improved
2718  double parang = (cpl_propertylist_get_double(header, "ESO ISS PARANG START")
2719  + cpl_propertylist_get_double(header, "ESO ISS PARANG END" ))/2. * (TWOPI/360.0); // in [rad]
2720  CPLCHECK_MSG ("Cannot get paralactic angle");
2721  cpl_msg_info (cpl_func,"FE: paralactic angle in degrees: %g ", parang * (360.0/TWOPI) );
2722 
2723  /* Posangle is calculated from SOBJX ansd SOBJY already read before
2724  x,y are exchanged following coordinate systems in Stefan's slide */
2725  int flag;
2726  double posang = myAtan (dy_in,dx_in, &flag);
2727  cpl_msg_info (cpl_func,"FE: position angle in degrees: %g ", posang / TWOPI * 360. );
2728 
2729  /* loop over all diodes and beams */
2730  for (int tel = 0; tel < ntel; tel++) {
2731 
2732  gravi_metrology_get_astig (header, tel, &AstigmAmplitude, &AstigmTheta, &AstigmRadius);
2733 
2734  for (cpl_size row = 0; row < nrow_met; row++) {
2735  for (int diode = 0; diode < ndiode; diode++) {
2736  diodeang = myAtan(-rec_zd[tel][diode],-rec_az[tel][diode], &flag); // [rad]
2737  astang = posang - parang - diodeang + AstigmTheta*(TWOPI/360.); // [rad]
2738  astradius = sqrt(rec_az[tel][diode]*rec_az[tel][diode] + rec_zd[tel][diode]*rec_zd[tel][diode]) / AstigmRadius; /* normalized */
2739  astigm = (AstigmAmplitude*1e-9) * sqrt(6) * astradius * astradius * sin(2. * astang); // [m]
2740 
2741  /* some debug messages */
2742  if (row == 0 && tel == 0 && diode == 0) {
2743  cpl_msg_info (cpl_func,"FE: Frank diode angle [deg]: %g", diodeang / TWOPI * 360.);
2744  cpl_msg_info (cpl_func,"FE: Frank astigmatism angle [deg]: %g", astang / TWOPI * 360.);
2745  cpl_msg_info (cpl_func,"FE: Frank normalized astradius: %g", astradius);
2746  cpl_msg_info (cpl_func,"FE: Frank astigmatism diode 0 in nm: %g", astigm*1e9);
2747  }
2748  if (row == 0 && tel == 0 && diode == 1) {
2749  cpl_msg_info (cpl_func,"FE: Frank astigmatism diode 1 in nm: %g", astigm*1e9);
2750  }
2751  if (row == 0 && tel == 0 && diode == 2) {
2752  cpl_msg_info (cpl_func,"FE: Frank astigmatism diode 2 in nm: %g", astigm*1e9);
2753  }
2754  if (row == 0 && tel == 0 && diode == 3) {
2755  cpl_msg_info (cpl_func,"FE: Frank astigmatism diode 3 in nm: %g", astigm*1e9);
2756  }
2757 
2758  /* apply astigmatism */
2759  opd_tel_corr[row*ntel+tel][diode] -= astigm;
2760  }
2761  }
2762  }
2763 
2764  /*****************************************************************/
2765  /* PART III: OPD_TELFC_CORR */
2766  /*****************************************************************/
2767  /* Calculation of the difference between the best correction of */
2768  /* the metrology receivers and the best correction of the fiber */
2769  /* coupler. This quantity should be close to zero. */
2770  /*****************************************************************/
2771 
2772  cpl_msg_info (cpl_func,"FE: calculate difference between corrected telescope diodes and fiber coupler in OPD_TELFC_CORR.");
2773 
2774  /* Create OPD_TELFC_CORR array in OI_VIS_MET table, fill with zeros, and get pointer */
2775  gravi_table_init_column_array (vismet_table, "OPD_TELFC_CORR", "m", CPL_TYPE_DOUBLE, ndiode);
2776  double ** opd_telfc_corr = gravi_table_get_data_array_double (vismet_table, "OPD_TELFC_CORR");
2777 
2778  /* Compute OPD_TELFC_CORR as (OPD_TEL + OPD_TEL_CORR) - (OPD_FC + OPD_FC_CORR) */
2779  /* going to complex phasor notation and then back to opd */
2780  double phi;
2781  double phifc;
2782  double complex phasor;
2783  double complex phasorfc;
2784  double complex dphasor;
2785 
2786  for (int tel = 0; tel < ntel; tel++) {
2787  for (cpl_size row = 0; row < nrow_met; row++) {
2788  phifc = (opd_fc[row*ntel+tel] + opd_fc_corr[row*ntel+tel])
2789  / lambda_met_mean * TWOPI; /* fiber coupler */
2790  phasorfc = cos(phifc) + sin(phifc) * I;
2791  for (int diode = 0; diode < ndiode; diode++) {
2792  phi = (opd_tel[row*ntel+tel][diode] + opd_tel_corr[row*ntel+tel][diode])
2793  / lambda_met_mean * TWOPI; /* telescope receiver */
2794  phasor = cos(phi) + sin(phi) * I;
2795  dphasor = phasor*conj(phasorfc);
2796  opd_telfc_corr[row*ntel+tel][diode] = carg(dphasor) / TWOPI * lambda_met_mean ;
2797  }
2798  }
2799  }
2800 
2801  /* wrap around median */
2802  cpl_vector *tmp_vector;
2803  tmp_vector = cpl_vector_new (nrow_met);
2804  double tmp_median;
2805  double low_limit;
2806  double high_limit;
2807 
2808  for (int tel = 0; tel < ntel; tel++) {
2809  for (int diode = 0; diode < ndiode; diode++) {
2810  /* fill tmp_vector with opd_telfc_corr for given telescope and diode */
2811  for (cpl_size row = 0; row < nrow_met; row++) {
2812  cpl_vector_set (tmp_vector, row, opd_telfc_corr[row*ntel+tel][diode]);
2813  }
2814  /* calculate median */
2815  tmp_median = cpl_vector_get_median_const(tmp_vector);
2816  high_limit = (tmp_median + lambda_met_mean / 2.);
2817  low_limit = (tmp_median - lambda_met_mean / 2.);
2818  /* wrap to median */
2819  for (cpl_size row = 0; row < nrow_met; row++) {
2820  if (opd_telfc_corr[row*ntel+tel][diode] > high_limit) {
2821  opd_telfc_corr[row*ntel+tel][diode] -= lambda_met_mean;
2822  }
2823  if (opd_telfc_corr[row*ntel+tel][diode] < low_limit) {
2824  opd_telfc_corr[row*ntel+tel][diode] += lambda_met_mean;
2825  }
2826  }
2827  /* do a second wrap around median for better estimate */
2828  for (cpl_size row = 0; row < nrow_met; row++) {
2829  cpl_vector_set (tmp_vector, row, opd_telfc_corr[row*ntel+tel][diode]);
2830  }
2831  tmp_median = cpl_vector_get_median_const(tmp_vector);
2832  high_limit = (tmp_median + lambda_met_mean / 2.);
2833  low_limit = (tmp_median - lambda_met_mean / 2.);
2834  for (cpl_size row = 0; row < nrow_met; row++) {
2835  if (opd_telfc_corr[row*ntel+tel][diode] > high_limit) {
2836  opd_telfc_corr[row*ntel+tel][diode] -= lambda_met_mean;
2837  }
2838  if (opd_telfc_corr[row*ntel+tel][diode] < low_limit) {
2839  opd_telfc_corr[row*ntel+tel][diode] += lambda_met_mean;
2840  }
2841  }
2842  }
2843  }
2844 
2845  double mmet[4][4];
2846 
2847  /* report final median for each telescope and diode */
2848  for (int tel = 0; tel < ntel; tel++) {
2849  for (int diode = 0; diode < ndiode; diode++) {
2850  /* fill tmp_vector with opd_telfc for given telescope and diode */
2851  for (cpl_size row = 0; row < nrow_met; row++) {
2852  cpl_vector_set (tmp_vector, row, opd_telfc_corr[row*ntel+tel][diode]);
2853  }
2854  /* calculate median */
2855  tmp_median = cpl_vector_get_median_const(tmp_vector);
2856  cpl_msg_info (cpl_func,"FE: median TEL-FC in nm for Tel %d Diode %d : %g ",
2857  tel, diode, tmp_median*1e9);
2858  /* FE: put results in a local variable for below residual tilt calculation */
2859  mmet[tel][diode] = tmp_median;
2860  }
2861  }
2862 
2863  /* FE: 2018-02-12: put QC parameters with residual Mean and simple TT and ASTIG estimates;
2864  simple means, not taking into acount detailed receiver positions, but working
2865  on sum and differences of opposite diodes;
2866  convention such that results give "residual in nm per diode"
2867  OFFS = Mean of all diodes
2868  = (D0 + D1 + D2 + D3) / 4
2869  TIP/TILT = 1/2 * difference of opposite diodes
2870  TIP = (D0 - D2) / 2
2871  TILT = (D1 - D3) / 2
2872  ASTIG = 1/2 * difference of Mean of opposite Diodes) =
2873  = [ (D0 + D2) / 2 - (D1 + D3) / 2 ] / 2
2874  = (D0 - D1 + D2 - D3) / 4
2875  */
2876 
2877  char qc_name[100];
2878  for (int tel=0; tel<ntel; tel++) {
2879  sprintf (qc_name, "ESO QC MET OFF%i", tel+1);
2880  cpl_msg_info (cpl_func, "%s = %f", qc_name, (mmet[tel][0]+mmet[tel][1]+mmet[tel][2]+mmet[tel][3])/4.*1e9);
2881  cpl_propertylist_update_double (header, qc_name, (mmet[tel][0]+mmet[tel][1]+mmet[tel][2]+mmet[tel][3])/4.*1e9);
2882  cpl_propertylist_set_comment (header, qc_name, "[nm] residual metrology offset");
2883  sprintf (qc_name, "ESO QC MET TIP%i", tel+1);
2884  cpl_msg_info (cpl_func, "%s = %f", qc_name, (mmet[tel][0]-mmet[tel][2])/2.*1e9);
2885  cpl_propertylist_update_double (header, qc_name, (mmet[tel][0]-mmet[tel][2])/2.*1e9);
2886  cpl_propertylist_set_comment (header, qc_name, "[nm] residual metrology tip");
2887  sprintf (qc_name, "ESO QC MET TILT%i", tel+1);
2888  cpl_msg_info (cpl_func, "%s = %f", qc_name, (mmet[tel][1]-mmet[tel][3])/2.*1e9);
2889  cpl_propertylist_update_double (header, qc_name, (mmet[tel][1]-mmet[tel][3])/2.*1e9);
2890  cpl_propertylist_set_comment (header, qc_name, "[nm] residual metrology tilt");
2891  sprintf (qc_name, "ESO QC MET ASTIG%i", tel+1);
2892  cpl_msg_info (cpl_func, "%s = %f", qc_name,(mmet[tel][0]-mmet[tel][1]+mmet[tel][2]-mmet[tel][3])/4.*1e9);
2893  cpl_propertylist_update_double (header, qc_name, (mmet[tel][0]-mmet[tel][1]+mmet[tel][2]-mmet[tel][3])/4.*1e9);
2894  cpl_propertylist_set_comment (header, qc_name, "[nm] residual metrology astigmatism");
2895  }
2896 
2897  /* FE: calculate residual tilt of metrology signal for each telescope and convert to
2898  equivalent dx,dy on detector. In other words, output the calibration error of fc_fiber_dxy
2899  */
2900 
2901  double mttx[4];
2902  double mtty[4];
2903  double mttn[4];
2904  double mtte[4];
2905  double mttdx[4];
2906  double mttdy[4];
2907  for (int tel = 0; tel < ntel; tel++) {
2908  mttx[tel] = (mmet[tel][3]*rec_az[tel][3]*pow(rec_zd[tel][0],2) - mmet[tel][0]*rec_az[tel][1]*rec_zd[tel][0]*rec_zd[tel][1] + mmet[tel][0]*rec_az[tel][0]*pow(rec_zd[tel][1],2) +
2909  mmet[tel][3]*rec_az[tel][3]*pow(rec_zd[tel][1],2) - mmet[tel][0]*rec_az[tel][2]*rec_zd[tel][0]*rec_zd[tel][2] + mmet[tel][0]*rec_az[tel][0]*pow(rec_zd[tel][2],2) +
2910  mmet[tel][3]*rec_az[tel][3]*pow(rec_zd[tel][2],2) - mmet[tel][3]*rec_az[tel][0]*rec_zd[tel][0]*rec_zd[tel][3] - mmet[tel][0]*rec_az[tel][3]*rec_zd[tel][0]*rec_zd[tel][3] -
2911  mmet[tel][3]*rec_az[tel][1]*rec_zd[tel][1]*rec_zd[tel][3] - mmet[tel][3]*rec_az[tel][2]*rec_zd[tel][2]*rec_zd[tel][3] + mmet[tel][0]*rec_az[tel][0]*pow(rec_zd[tel][3],2) -
2912  mmet[tel][2]*rec_zd[tel][2]*(rec_az[tel][0]*rec_zd[tel][0] + rec_az[tel][1]*rec_zd[tel][1] + rec_az[tel][3]*rec_zd[tel][3]) -
2913  mmet[tel][1]*rec_zd[tel][1]*(rec_az[tel][0]*rec_zd[tel][0] + rec_az[tel][2]*rec_zd[tel][2] + rec_az[tel][3]*rec_zd[tel][3]) +
2914  mmet[tel][2]*rec_az[tel][2]*(pow(rec_zd[tel][0],2) + pow(rec_zd[tel][1],2) + pow(rec_zd[tel][3],2)) +
2915  mmet[tel][1]*rec_az[tel][1]*(pow(rec_zd[tel][0],2) + pow(rec_zd[tel][2],2) + pow(rec_zd[tel][3],2))) /
2916  (pow(rec_az[tel][3],2)*pow(rec_zd[tel][0],2) + pow(rec_az[tel][0],2)*pow(rec_zd[tel][1],2) +
2917  pow(rec_az[tel][3],2)*pow(rec_zd[tel][1],2) + pow(rec_az[tel][0],2)*pow(rec_zd[tel][2],2) +
2918  pow(rec_az[tel][3],2)*pow(rec_zd[tel][2],2) - 2*rec_az[tel][0]*rec_az[tel][3]*rec_zd[tel][0]*rec_zd[tel][3] +
2919  pow(rec_az[tel][0],2)*pow(rec_zd[tel][3],2) - 2*rec_az[tel][2]*rec_zd[tel][2]*(rec_az[tel][0]*rec_zd[tel][0] + rec_az[tel][3]*rec_zd[tel][3]) -
2920  2*rec_az[tel][1]*rec_zd[tel][1]*(rec_az[tel][0]*rec_zd[tel][0] + rec_az[tel][2]*rec_zd[tel][2] + rec_az[tel][3]*rec_zd[tel][3]) +
2921  pow(rec_az[tel][2],2)*(pow(rec_zd[tel][0],2) + pow(rec_zd[tel][1],2) + pow(rec_zd[tel][3],2)) +
2922  pow(rec_az[tel][1],2)*(pow(rec_zd[tel][0],2) + pow(rec_zd[tel][2],2) + pow(rec_zd[tel][3],2)));
2923  mtty[tel] = (-(mmet[tel][2]*rec_az[tel][0]*rec_az[tel][2]*rec_zd[tel][0]) - mmet[tel][3]*rec_az[tel][0]*rec_az[tel][3]*rec_zd[tel][0] - mmet[tel][2]*rec_az[tel][1]*rec_az[tel][2]*rec_zd[tel][1] -
2924  mmet[tel][3]*rec_az[tel][1]*rec_az[tel][3]*rec_zd[tel][1] + mmet[tel][2]*pow(rec_az[tel][0],2)*rec_zd[tel][2] + mmet[tel][2]*pow(rec_az[tel][1],2)*rec_zd[tel][2] -
2925  mmet[tel][3]*rec_az[tel][2]*rec_az[tel][3]*rec_zd[tel][2] + mmet[tel][2]*pow(rec_az[tel][3],2)*rec_zd[tel][2] + mmet[tel][3]*pow(rec_az[tel][0],2)*rec_zd[tel][3] +
2926  mmet[tel][3]*pow(rec_az[tel][1],2)*rec_zd[tel][3] + mmet[tel][3]*pow(rec_az[tel][2],2)*rec_zd[tel][3] - mmet[tel][2]*rec_az[tel][2]*rec_az[tel][3]*rec_zd[tel][3] +
2927  mmet[tel][0]*(pow(rec_az[tel][1],2)*rec_zd[tel][0] + pow(rec_az[tel][2],2)*rec_zd[tel][0] + pow(rec_az[tel][3],2)*rec_zd[tel][0] -
2928  rec_az[tel][0]*rec_az[tel][1]*rec_zd[tel][1] - rec_az[tel][0]*rec_az[tel][2]*rec_zd[tel][2] - rec_az[tel][0]*rec_az[tel][3]*rec_zd[tel][3]) +
2929  mmet[tel][1]*(-(rec_az[tel][0]*rec_az[tel][1]*rec_zd[tel][0]) + pow(rec_az[tel][0],2)*rec_zd[tel][1] + pow(rec_az[tel][2],2)*rec_zd[tel][1] +
2930  pow(rec_az[tel][3],2)*rec_zd[tel][1] - rec_az[tel][1]*rec_az[tel][2]*rec_zd[tel][2] - rec_az[tel][1]*rec_az[tel][3]*rec_zd[tel][3]))/
2931  (pow(rec_az[tel][3],2)*pow(rec_zd[tel][0],2) + pow(rec_az[tel][0],2)*pow(rec_zd[tel][1],2) +
2932  pow(rec_az[tel][3],2)*pow(rec_zd[tel][1],2) + pow(rec_az[tel][0],2)*pow(rec_zd[tel][2],2) +
2933  pow(rec_az[tel][3],2)*pow(rec_zd[tel][2],2) - 2*rec_az[tel][0]*rec_az[tel][3]*rec_zd[tel][0]*rec_zd[tel][3] +
2934  pow(rec_az[tel][0],2)*pow(rec_zd[tel][3],2) - 2*rec_az[tel][2]*rec_zd[tel][2]*(rec_az[tel][0]*rec_zd[tel][0] + rec_az[tel][3]*rec_zd[tel][3]) -
2935  2*rec_az[tel][1]*rec_zd[tel][1]*(rec_az[tel][0]*rec_zd[tel][0] + rec_az[tel][2]*rec_zd[tel][2] + rec_az[tel][3]*rec_zd[tel][3]) +
2936  pow(rec_az[tel][2],2)*(pow(rec_zd[tel][0],2) + pow(rec_zd[tel][1],2) + pow(rec_zd[tel][3],2)) +
2937  pow(rec_az[tel][1],2)*(pow(rec_zd[tel][0],2) + pow(rec_zd[tel][2],2) + pow(rec_zd[tel][3],2)));
2938  // above should be in radians , convert to mas
2939  mttx[tel] = mttx[tel] / TWOPI * 360. * 3600. * 1000.;
2940  mtty[tel] = mtty[tel] / TWOPI * 360. * 3600. * 1000.;
2941  // now convert tp pixel above should be in radians, divide by pixel scale to get pixel
2942  // FE 2018-02-09 using default 18 mas/pixel from UTs, because division by 0.0 would cause NAN problems below
2943  // double scale = 0.0;
2944  double scale = 18.0;
2945  sprintf (card,"ESO QC ACQ FIELD%d SCALE", tel+1);
2946  if (cpl_propertylist_has (header, card))
2947  scale = cpl_propertylist_get_double (header, card);
2948 
2949  // FE 2018-02-09 check if telescope given, otherwise
2950  // receiver pos are all zero and we get NaN
2951  /* Get telname */
2952  const char * telname = gravi_conf_get_telname (tel, header);
2953  if (telname == NULL) {
2954  mttx[tel] = 0.;
2955  mtty[tel] = 0.;
2956  }
2957 
2958  mttx[tel] = mttx[tel] / scale;
2959  mtty[tel] = mtty[tel] / scale;
2960  // rotate to sky (north, east) using parang
2961  mtte[tel] = mttx[tel]*cos(parang) + mtty[tel]*sin(parang);
2962  mttn[tel] = - mttx[tel]*sin(parang) + mtty[tel]*cos(parang);
2963  // rotate to acqcam
2964  double northangle = gravi_pfits_get_fangle_acqcam (header, tel);
2965  mttdx[tel] = mttn[tel]*sin(northangle * CPL_MATH_RAD_DEG ) + mtte[tel]*cos(northangle * CPL_MATH_RAD_DEG );
2966  mttdy[tel] = mttn[tel]*cos(northangle * CPL_MATH_RAD_DEG ) - mtte[tel]*sin(northangle * CPL_MATH_RAD_DEG );
2967  }
2968 
2969  cpl_msg_info (cpl_func,"FE: mttx, mtty in uvw coordinates of GV1: %g %g pixel", mttx[0], mtty[0]);
2970  cpl_msg_info (cpl_func,"FE: mttx, mtty in uvw coordinates of GV2: %g %g pixel", mttx[1], mtty[1]);
2971  cpl_msg_info (cpl_func,"FE: mttx, mtty in uvw coordinates of GV3: %g %g pixel", mttx[2], mtty[2]);
2972  cpl_msg_info (cpl_func,"FE: mttx, mtty in uvw coordinates of GV4: %g %g pixel", mttx[3], mtty[3]);
2973  cpl_msg_info (cpl_func,"FE: mttx, mtty in RA DEC coordinates of GV1: %g %g pixel", mtte[0], mttn[0]);
2974  cpl_msg_info (cpl_func,"FE: mttx, mtty in RA DEC coordinates of GV2: %g %g pixel", mtte[1], mttn[1]);
2975  cpl_msg_info (cpl_func,"FE: mttx, mtty in RA DEC coordinates of GV3: %g %g pixel", mtte[2], mttn[2]);
2976  cpl_msg_info (cpl_func,"FE: mttx, mtty in RA DEC coordinates of GV4: %g %g pixel", mtte[3], mttn[3]);
2977  cpl_msg_info (cpl_func,"FE: correction for dxy of GV1: %g %g pixel", mttdx[0], mttdy[0]);
2978  cpl_msg_info (cpl_func,"FE: correction for dxy of GV2: %g %g pixel", mttdx[1], mttdy[1]);
2979  cpl_msg_info (cpl_func,"FE: correction for dxy of GV3: %g %g pixel", mttdx[2], mttdy[2]);
2980  cpl_msg_info (cpl_func,"FE: correction for dxy of GV4: %g %g pixel", mttdx[3], mttdy[3]);
2981 
2982  /* Add QC parameters for the corrections */
2983  // FE: declared above
2984  // char qc_name[100];
2985  for (int tel=0; tel<ntel; tel++) {
2986  sprintf (qc_name, "ESO QC MET FIBER SC%iDX", tel+1);
2987  cpl_msg_info (cpl_func, "%s = %f", qc_name, mttdx[tel]);
2988  cpl_propertylist_update_double (header, qc_name, mttdx[tel]);
2989  cpl_propertylist_set_comment (header, qc_name, "[pix] SC fiber offset measured by tel metrology");
2990  sprintf (qc_name, "ESO QC MET FIBER SC%iDY", tel+1);
2991  cpl_msg_info (cpl_func, "%s = %f", qc_name, mttdy[tel]);
2992  cpl_propertylist_update_double (header, qc_name, mttdy[tel]);
2993  cpl_propertylist_set_comment (header, qc_name, "[pix] SC fiber offset measured by tel metrology");
2994  }
2995 
2996  /* FE 2018-02-10 Calculate best guess offset of SC fiber from
2997  desired SOBJXY given by SC_FIBER_DX - QC.MET.FIBER.SCiDX:
2998  sobj_dx/y in pixel on acqcam
2999  sobj_dra/dec in mas on sky
3000  sign convention is such that sobj_dxy is identical to value measured by acqcam
3001  when residual metrology tilt is zero, and - QC.MET.FIBER.SCiDX in case the acqcam
3002  doesn't see any error, but the metrology does */
3003 
3004  double sobj_dx[4]; /* total offset in pixel in acqcam coordinates */
3005  double sobj_dy[4]; /* total offset in pixel in acqcam coordinates */
3006  double sobj_dra[4]; /* total RA offset in mas */
3007  double sobj_ddec[4]; /* total DEC offset in mas */
3008  double sc_fiber_dx[4]; /* pixel offset measured by AcqCam (without taking into account metrology tilt) in pixel */
3009  double sc_fiber_dy[4]; /* pixel offset measured by AcqCam (without taking into account metrology tilt) in pixel */
3010 
3011  for (int tel=0; tel<ntel; tel++) {
3012 
3013  /* in acquisition camera X/Y pixel */
3014  sprintf (card,"ESO QC ACQ FIELD%d SC_FIBER_DX", tel+1);
3015  if (cpl_propertylist_has (header, card)) {
3016  sc_fiber_dx[tel] = cpl_propertylist_get_double (header, card);
3017  sobj_dx[tel] = sc_fiber_dx[tel] - mttdx[tel];
3018  } else {
3019  sobj_dx[tel] = - mttdx[tel];
3020  }
3021  sprintf (card,"ESO QC ACQ FIELD%d SC_FIBER_DY", tel+1);
3022  if (cpl_propertylist_has (header, card)) {
3023  sc_fiber_dy[tel] = cpl_propertylist_get_double (header, card);
3024  sobj_dy[tel] = sc_fiber_dy[tel] - mttdy[tel];
3025  } else {
3026  sobj_dy[tel] = - mttdy[tel];
3027  }
3028 
3029  sprintf (qc_name, "ESO QC MET SOBJ DX%i", tel+1);
3030  cpl_msg_info (cpl_func, "%s = %f", qc_name, sobj_dx[tel]);
3031  cpl_propertylist_update_double (header, qc_name, sobj_dx[tel]);
3032  cpl_propertylist_set_comment (header, qc_name, "[pixel] x offset from SOBJ");
3033  sprintf (qc_name, "ESO QC MET SOBJ DY%i", tel+1);
3034  cpl_msg_info (cpl_func, "%s = %f", qc_name, sobj_dy[tel]);
3035  cpl_propertylist_update_double (header, qc_name, sobj_dy[tel]);
3036  cpl_propertylist_set_comment (header, qc_name, "[pixel] y offset from SOBJ");
3037 
3038  /* on Sky in RA/DEC mas, undoing above ttdx calculation now for
3039  offset including offset measured on acqcam */
3040  // rotate from acqcam to sky
3041  // FE 2018-02-10: below declaration should be done one in the beginning
3042  double northangle = gravi_pfits_get_fangle_acqcam (header, tel);
3043  sobj_dra[tel] = sobj_dx[tel]*cos(northangle * CPL_MATH_RAD_DEG ) - sobj_dy[tel]*sin(northangle * CPL_MATH_RAD_DEG );
3044  sobj_ddec[tel] = sobj_dx[tel]*sin(northangle * CPL_MATH_RAD_DEG ) + sobj_dy[tel]*cos(northangle * CPL_MATH_RAD_DEG );
3045  // convert to mas
3046  // FE 2018-02-10 using the same dafault as above, if no acqcam measurement available
3047  double scale = 18.0;
3048  sprintf (card,"ESO QC ACQ FIELD%d SCALE", tel+1);
3049  if (cpl_propertylist_has (header, card))
3050  scale = cpl_propertylist_get_double (header, card);
3051  sobj_dra[tel] = sobj_dra[tel] * scale;
3052  sobj_ddec[tel] = sobj_ddec[tel] * scale;
3053 
3054  sprintf (qc_name, "ESO QC MET SOBJ DRA%i", tel+1);
3055  cpl_msg_info (cpl_func, "%s = %f", qc_name, sobj_dra[tel]);
3056  cpl_propertylist_update_double (header, qc_name, sobj_dra[tel]);
3057  cpl_propertylist_set_comment (header, qc_name, "[mas] RA offset from SOBJ");
3058  sprintf (qc_name, "ESO QC MET SOBJ DDEC%i", tel+1);
3059  cpl_msg_info (cpl_func, "%s = %f", qc_name, sobj_ddec[tel]);
3060  cpl_propertylist_update_double (header, qc_name, sobj_ddec[tel]);
3061  cpl_propertylist_set_comment (header, qc_name, "[mas] DEC offset from SOBJ");
3062  }
3063 
3064 
3065  /*****************************************************************/
3066  /* PART IV: OPD_TELFC_MCORR */
3067  /*****************************************************************/
3068  /* Average the four telescope diodes. */
3069  /* Note: could also be other combinations, e.g. the less noisy */
3070  /* mean of opposite diodes. */
3071  /*****************************************************************/
3072 
3073 
3074  cpl_msg_info (cpl_func,"FE: calculate OPD_TELFC_MCORR.");
3075 
3076  /* Create array in OI_VIS_MET table, fill with zeros, and get pointer */
3077  gravi_table_new_column (vismet_table, "OPD_TELFC_MCORR", "m", CPL_TYPE_DOUBLE);
3078  double * opd_telfc_mcorr = cpl_table_get_data_double (vismet_table, "OPD_TELFC_MCORR");
3079 
3080  /* Calculate mean correction and store in table */
3081  for (int tel = 0; tel < ntel; tel++) {
3082  for (cpl_size row = 0; row < nrow_met; row++) {
3083  opd_telfc_mcorr[row*ntel+tel] =
3084  (opd_telfc_corr[row*ntel+tel][0]
3085  + opd_telfc_corr[row*ntel+tel][1]
3086  + opd_telfc_corr[row*ntel+tel][2]
3087  + opd_telfc_corr[row*ntel+tel][3]) / 4. ;
3088  }
3089  }
3090 
3091  FREE (cpl_vector_delete, tmp_vector);
3092 
3093  cpl_msg_info (cpl_func,"FE: end.");
3094 
3095  /*----------------------------------------------------------------*/
3096  /* FE end */
3097  /*----------------------------------------------------------------*/
3098 
3099  CPLCHECK_MSG ("Cannot fill result of TEL vs FC computation");
3100 
3101  /* Free the pointer to pointer to data */
3102  FREE (cpl_free, opd_tel);
3103  FREE (cpl_free, opd_tel_corr);
3104  FREE (cpl_free, opd_telfc_corr);
3105 
3106  gravi_msg_function_exit(1);
3107  return CPL_ERROR_NONE;
3108 }
3109 
3110 
3111 /* -------------------------------------------------------------------- */
3121 /* -------------------------------------------------------------------- */
3122 static int sin_lambda(const double x[], const double a[], double *result){
3123 
3124  *result = a[0] + a[1] * sin(x[0] * (2 * M_PI) / a[3]) +
3125  a[2] * cos(x[0] * (2 * M_PI) / a[3]);
3126 
3127  return (0);
3128 }
3129 
3130 static int dfda_sin(const double x[], const double a[], double result[]){
3131  result[0] = 1;
3132  result[1] = sin(x[0] * (2 * M_PI) / a[3]);
3133  result[2] = cos(x[0] * (2 * M_PI) / a[3]);
3134  result[3] = a[1] * x[0] * (2 * M_PI) / (a[3] * a[3]) *
3135  sin(x[0] * (2 * M_PI) / a[3]) - a[1] * x[0] *
3136  (2 * M_PI) / (a[3] * a[3]) * cos(x[0] * (2 * M_PI) / a[3]);
3137  return (0);
3138 }
3139 
3140 /* -------------------------------------------------------------------- */
3154 /* -------------------------------------------------------------------- */
3155 
3156 cpl_table * gravi_metrology_compute_p2vm (cpl_table * metrology_table, double wave_met)
3157 {
3158  gravi_msg_function_start(1);
3159  cpl_ensure (metrology_table, CPL_ERROR_NULL_INPUT, NULL);
3160 
3161  cpl_vector * vectA, * vectB,
3162  * y_sigma, * init_val, *opl_vect;
3163  const cpl_array * volt;
3164  cpl_array * coherence_fit, * phase_fit;
3165  cpl_matrix * opd_matrix;
3166  int infos = 0;
3167  cpl_vector ** vect;
3168  int val_to_fit2[] = {1,1,1,0}, nv;
3169  double mse, red_chisq;
3170  cpl_array * trans;
3171 
3172  int ntel = 4 ;
3173  int n_diode = 80;
3174 
3175  /* Get data */
3176  cpl_size nrow = cpl_table_get_nrow (metrology_table);
3177  CPLCHECK_NUL ("Cannot get data");
3178 
3179  /* Create p2vm_met : (REGNAME, TRANSMISSION, COHERENCE and PHASE) */
3180  cpl_table * p2vm_met = cpl_table_new (ntel * 2);
3181  cpl_table_new_column_array (p2vm_met,"TRANSMISSION", CPL_TYPE_DOUBLE, 2);
3182  cpl_table_new_column_array (p2vm_met,"COHERENCE", CPL_TYPE_DOUBLE, 2);
3183  cpl_table_new_column_array (p2vm_met,"PHASE", CPL_TYPE_DOUBLE, 2);
3184 
3185  CPLCHECK_NUL ("Allocate the metrology output");
3186 
3187  /* Loop on SC and FT */
3188  for (int gravi_type = 0; gravi_type < 2; gravi_type++ ){
3189  int comb = (gravi_type == GRAVI_SC ? 1 : 2);
3190 
3191  /* Loop on diodes */
3192  for (int tel = 0; tel < ntel; tel++){
3193 
3194  /* load vectA and vectB from metrology */
3195  vectA = cpl_vector_new (nrow);
3196  vectB = cpl_vector_new (nrow);
3197 
3198  for (cpl_size row = 0; row < nrow; row ++){
3199  volt = cpl_table_get_array ( metrology_table, "VOLT", row);
3200  cpl_vector_set (vectA, row, cpl_array_get (volt , n_diode - 2*(comb*ntel - tel), &nv));
3201  cpl_vector_set (vectB, row, cpl_array_get (volt , n_diode - 2*(comb*ntel - tel) + 1, &nv));
3202  }
3203 
3204  /* Compute phase from vectA and vectB*/
3205  opl_vect = gravi_ellipse_phase_create (vectA, vectB, NULL);
3206  cpl_vector_multiply_scalar (opl_vect, wave_met/(2.0*M_PI));
3207  CPLCHECK_NUL("Compute OPD");
3208 
3209  /* put OPD into opd_matrix for P2VM_MET calib */
3210  opd_matrix = cpl_matrix_new(nrow, 1);
3211  for (cpl_size row = 0; row < nrow; row++){
3212  cpl_matrix_set (opd_matrix, row, 0, cpl_vector_get (opl_vect, row));
3213  }
3214 
3215  cpl_vector_delete (opl_vect);
3216 
3217  /* fit on a central window of 5*lambda width */
3218 
3219  vect = cpl_malloc (2*sizeof(cpl_vector*));
3220  vect[0] = vectA;
3221  vect[1] = vectB;
3222  phase_fit = cpl_array_new (2, CPL_TYPE_DOUBLE);
3223  coherence_fit = cpl_array_new (2, CPL_TYPE_DOUBLE);
3224  trans = cpl_array_new (2, CPL_TYPE_DOUBLE);
3225 
3226  for (int i = 0; i < 2; i++){
3227 
3228  /* Get the spectrum of the vector region */
3229  y_sigma = cpl_vector_new(nrow);
3230  cpl_vector_fill(y_sigma, 1);
3231 
3232  /* Define and initialize all variables to make a FIT */
3233  mse = 0;
3234  red_chisq = 0;
3235  init_val = cpl_vector_new(4);
3236  cpl_vector_set(init_val, 0, 1);
3237  cpl_vector_set(init_val, 1, 1);
3238  cpl_vector_set(init_val, 2, 1);
3239  cpl_vector_set(init_val, 3, wave_met);
3240 
3241  cpl_errorstate prestate = cpl_errorstate_get();
3242  cpl_fit_lvmq(opd_matrix, NULL, vect[i],
3243  y_sigma, init_val, val_to_fit2, &sin_lambda,
3244  &dfda_sin, CPL_FIT_LVMQ_TOLERANCE, CPL_FIT_LVMQ_COUNT,
3245  CPL_FIT_LVMQ_MAXITER, &mse, &red_chisq, NULL);
3246 
3247  if (cpl_error_get_code()){
3248  printf("error %f %s and %s \n", 6.0, cpl_error_get_message(), cpl_error_get_where());
3249  return NULL;
3250  }
3251 
3252  if (!strcmp("The iterative process did not converge",
3253  cpl_error_get_message())){
3254  if (infos)
3255  cpl_msg_info(cpl_func, "The iterative process "
3256  "did not converge");
3257  cpl_errorstate_set (prestate);
3258  }
3259  if (infos)
3260  cpl_msg_info(cpl_func, "tel = %d : mse "
3261  "%g chi2 %g", tel, mse, red_chisq);
3262 
3263 
3264  /* Compute the P2VM value */
3265  cpl_array_set_double (coherence_fit, i,
3266  sqrt( pow( cpl_vector_get(init_val, 2), 2) +
3267  pow( cpl_vector_get(init_val, 1), 2)));
3268 
3269  cpl_array_set_double (phase_fit, i, atan2( cpl_vector_get(init_val, 2),
3270  cpl_vector_get(init_val, 1)));
3271 
3272  cpl_array_set_double (trans, i, cpl_vector_get(init_val, 0));
3273 
3274  if (cpl_error_get_code()){
3275  printf("error %f %s and %s \n", 7.0, cpl_error_get_message(), cpl_error_get_where());
3276  return NULL;
3277  }
3278 
3279  cpl_vector_delete(init_val);
3280  cpl_vector_delete(y_sigma);
3281  cpl_vector_delete (vect[i]);
3282  }
3283 
3284  FREE (cpl_free, vect);
3285  FREE (cpl_matrix_delete, opd_matrix);
3286 
3287  cpl_table_set_array (p2vm_met, "COHERENCE", ntel*(1-gravi_type) + tel, coherence_fit);
3288  cpl_array_subtract_scalar (phase_fit,
3289  cpl_array_get_double (phase_fit, 0, &nv));
3290  cpl_table_set_array (p2vm_met, "PHASE", ntel*(1-gravi_type) + tel, phase_fit);
3291  cpl_table_set_array (p2vm_met, "TRANSMISSION", ntel*(1-gravi_type) + tel, trans);
3292 
3293  FREE (cpl_array_delete, trans);
3294  FREE (cpl_array_delete, coherence_fit);
3295  FREE (cpl_array_delete, phase_fit);
3296  CPLCHECK_NUL("End loop on tel");
3297  }
3298  }
3299 
3300  /* Verbose */
3301  gravi_msg_function_exit(1);
3302  return p2vm_met;
3303 }
3304 
3305 
3306 /* -------------------------------------------------------------------- */
3322 /* -------------------------------------------------------------------- */
3323 cpl_error_code gravi_metrology_reduce (gravi_data * data,
3324  gravi_data * eop_data,
3325  gravi_data * static_param_data,
3326  gravi_data * met_pos,
3327  const cpl_parameterlist * parlist)
3328 {
3329  gravi_msg_function_start(1);
3330  cpl_ensure_code (data, CPL_ERROR_NULL_INPUT);
3331 
3332  /* Load data */
3333  cpl_table * metrology_table = gravi_data_get_table (data, GRAVI_METROLOGY_EXT);
3334  cpl_propertylist * header = gravi_data_get_header (data);
3335  CPLCHECK_MSG ("Cannot load met extension");
3336 
3337  /* Update receiver position */
3338  if (met_pos) {
3339  cpl_table * pos_table = gravi_data_get_table (met_pos, "RECEIVER_POSITION");
3340  gravi_metrology_update_receiverpos (header, pos_table);
3341  CPLCHECK_MSG ("Cannot update receiver positions");
3342  }
3343 
3344  /* Create the table */
3345  cpl_table * vismet_table = NULL;
3346  vismet_table = gravi_metrology_create (metrology_table, header);
3347  CPLCHECK_MSG ("Cannot create vismet_table");
3348 
3349  /* Compute pointing directions, but do not calculate projected baseline */
3350  int save_pointing = 1;
3351  gravi_eop_pointing_uv (vismet_table, header,
3352  (eop_data ? gravi_data_get_table_x (eop_data, 0) : NULL),
3353  (eop_data ? gravi_data_get_header (eop_data) : NULL),
3354  save_pointing, NULL);
3355 
3356  /* If VIS_ACQ table exist, we compute the OPD_PUPIL */
3357  if (gravi_data_has_extension (data, GRAVI_OI_VIS_ACQ_EXT)) {
3358  cpl_table * visacq_table;
3359  visacq_table = gravi_data_get_table (data, GRAVI_OI_VIS_ACQ_EXT);
3360  double delay = gravi_param_get_double_default (parlist, "gravity.metrology.acq-correction-delay",0.0);
3361  gravi_metrology_acq (visacq_table, vismet_table, delay, header);
3362  }
3363 
3364  /* Reduce the metrology with the DRS algorithm, this
3365  * creates the OI_VIS_MET table */
3366  gravi_metrology_drs (metrology_table, vismet_table, header);
3367  CPLCHECK_MSG ("Cannot reduce metrology with DRS algo");
3368 
3369  /* Add the columns from TAC algorithm */
3370  gravi_metrology_tac (metrology_table, vismet_table, header);
3371  CPLCHECK_MSG ("Cannot reduce metrology with TAC algo");
3372 
3373  /* Compute TEL vs FC corrections */
3374  gravi_metrology_telfc (metrology_table, vismet_table, static_param_data, header);
3375  CPLCHECK_MSG ("Cannot compute TEL vs FC reference");
3376 
3377  /* Add the VISMET_TABLE table to the gravi_data */
3378  gravi_data_add_table (data, NULL, GRAVI_OI_VIS_MET_EXT, vismet_table);
3379  CPLCHECK_MSG ("Cannot add OI_VIS_MET in p2vmred_data");
3380 
3381  gravi_msg_function_exit(1);
3382  return CPL_ERROR_NONE;
3383 }
3384 
cpl_error_code gravi_metrology_tac(cpl_table *metrology_table, cpl_table *vismet_table, cpl_propertylist *header)
Compute the metrology signal from TAC algorithm.
double gravi_metrology_get_fc_focus(cpl_propertylist *header, int gv, gravi_data *static_param_data)
Read the fiber coupler focus offset from the fits header.
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_metrology_create(cpl_table *metrology_table, cpl_propertylist *header)
Create the VIS_MET table.
cpl_error_code gravi_metrology_update_receiverpos(cpl_propertylist *header, cpl_table *receiver_table)
Update the receiver position from header from external calibration.
cpl_vector * gravi_ellipse_phase_create(cpl_vector *vectCA, cpl_vector *vectDB, cpl_vector *envelope)
Compute the phase atan{X&#39;,Y&#39;}, unwraped from first sample.
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_error_code gravi_metrology_acq(cpl_table *visacq_table, cpl_table *vismet_table, double delay, cpl_propertylist *header)
Fill the VIS_MET table with the OPD_PUPIL column.
double gravi_param_get_double_default(const cpl_parameterlist *parlist, const char *name, double def)
Get the parameter from the parameter list.
Definition: gravi_dfs.c:1045
cpl_error_code gravi_metrology_drs(cpl_table *metrology_table, cpl_table *vismet_table, cpl_propertylist *header)
Fill the VIS_MET table with the DRS algorithm.
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_table * gravi_data_get_table_x(gravi_data *self, int i)
Get the table of an extension by position.
Definition: gravi_data.c:1536
double gravi_metrology_get_fc_shift(cpl_propertylist *header, int gv, gravi_data *default_focus_data)
Read the fiber coupler focus offset from the fits header.
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_error_code gravi_metrology_reduce(gravi_data *data, gravi_data *eop_data, gravi_data *static_param_data, gravi_data *met_pos, const cpl_parameterlist *parlist)
Reduce the metrology.
cpl_table * gravi_metrology_compute_p2vm(cpl_table *metrology_table, double wave_met)
Calibrate the P2VM of the metrology.
double gravi_metrology_get_posx(cpl_propertylist *header, int tel, int diode)
Read the receiver position from header.
cpl_error_code gravi_metrology_get_astig(cpl_propertylist *header, int gv, double *ampltiude, double *angle, double *radius)
Read the astigmatism amplitude and angle from the fits header.
cpl_error_code gravi_signal_create_sync(cpl_table *vis_SC, int nbase_sc, double dit_sc, cpl_table *vis_FT, int nbase_ft, const char *name)
Compute synchronisation indices between OIFITS tables.
Definition: gravi_signal.c:743
cpl_error_code gravi_metrology_telfc(cpl_table *metrology_table, cpl_table *vismet_table, gravi_data *static_param_data, cpl_propertylist *header)
Best knowledge correction for referencing TEL to FC.