Commits
C. Enrique Garcia Dabo authored and Ville Suoranta committed d992c3455d1 Merge
1 - | from __future__ import absolute_import |
2 - | import time |
3 - | import numpy as np |
4 - | import sys |
5 - | |
6 - | from casatasks.private.casa_transition import is_CASA6 |
7 - | if is_CASA6: |
8 - | from casatools import table |
9 - | from casatasks import casalog |
10 - | |
11 - | tb_tool = table( ) |
12 - | else: |
13 - | from taskinit import * |
14 - | |
15 - | (tb_tool,) = gentools(['tb']) |
16 - | |
17 - | def _fetch_tmcdb_info(ant_names, obs_time): |
18 - | use_soap = False |
19 - | if use_soap: |
20 - | response = query_tmcdb_antennas_soap(ant_names, obs_time) |
21 - | return process_tmcdb_response_soap_for_gencal(response) |
22 - | else: |
23 - | response = query_tmcdb_antennas_rest(ant_names, obs_time) |
24 - | return process_tmcdb_response_rest_for_gencal(ant_names, response) |
25 - | |
26 - | |
27 - | def query_tmcdb_antennas_rest(ant_names, timestamp): |
28 - | """ |
29 - | Queries the new REST web service for positions of a list of antennas at a given |
30 - | timestamp. |
31 - | |
32 - | REST service deployed for testing https://2018may.asa-test.alma.cl/antenna-position |
33 - | Service doc: |
34 - | https://bitbucket.sco.alma.cl/projects/ALMA/repos/almasw/browse/CONTROL-SERVICES/PositionsService |
35 - | Swagger TMCDB Positions API doc: |
36 - | https://2018may.asa-test.alma.cl/antenna-position/swagger-ui.html |
37 - | |
38 - | Example query: |
39 - | https://2018may.asa-test.alma.cl/antenna-position/position/antenna?configuration=CURRENT.AOS&antenna=DV10×tamp=2015-04-19T16:53:54.000 |
40 - | |
41 - | :param ant_names: list of antenna names as strings |
42 - | :param timestamp: timestamp specification string (in '2017-01-01T16:53:54.000' format) |
43 - | |
44 - | :returns: list of per antenna responses as retrieved from the web service |
45 - | """ |
46 - | import time |
47 - | import requests |
48 - | |
49 - | TEST_HOSTNAME = 'https://2018may.asa-test.alma.cl' |
50 - | |
51 - | hostname = TEST_HOSTNAME |
52 - | port = 443 |
53 - | api = 'antenna-position/position/antenna' |
54 - | # To have the .milliseconds (which is not included in the MS obs_time), like: |
55 - | # 2015-04-19T16:53:54.000 |
56 - | tstamp_ms = '{}.000'.format(timestamp) |
57 - | ant_responses = [] |
58 - | |
59 - | casalog.post('Querying TMC DB, positions for these antennas: {0}, at time: {1}'. |
60 - | format(ant_names, tstamp_ms), 'INFO') |
61 - | time_start = time.time() |
62 - | try: |
63 - | for aname in ant_names: |
64 - | url = '{}:{}/{}?antenna={}×tamp={}'.format(hostname, port, api, aname, |
65 - | tstamp_ms) |
66 - | resp = requests.get(url) |
67 - | casalog.post('Queried antenna "{0}". Response status: {1}. Response text: {2}'. |
68 - | format(aname, resp.status_code, resp.text), 'DEBUG2') |
69 - | ant_responses.append(resp) |
70 - | |
71 - | time_end = time.time() |
72 - | casalog.post('Got responses from TMC DB. Took {0}s'.format(time_end - time_start)) |
73 - | except RuntimeError as exc: |
74 - | casalog.post('Exception while querying the TMC DB for antenna positions information:' |
75 - | '{0}'.format(exc), 'ERROR') |
76 - | raise |
77 - | |
78 - | return ant_responses |
79 - | |
80 - | |
81 - | def process_tmcdb_response_rest_for_gencal(ant_names, responses): |
82 - | """ |
83 - | Takes a list of response from the TMCDB Positions service (REST) |
84 - | and produces a list of antenna names and a list of their position |
85 - | correction parameters. |
86 - | |
87 - | :param resp: names of the antennas |
88 - | :param resp: response objects from web service (some might be error or |
89 - | incomplete) |
90 - | |
91 - | :returns: a tuple with three elements. The tuple has |
92 - | as first element a list of antenna names as strings, and as |
93 - | second element a vector of positions (as Bx, By, Bz) for |
94 - | every antenna. The third element holds the positions of the |
95 - | corresponding pads. |
96 - | """ |
97 - | |
98 - | def get_ant_pad_position(ant_name, resp): |
99 - | import json |
100 - | pos_found = False |
101 - | if 200 == resp.status_code: |
102 - | json_resp = json.loads(resp.text) |
103 - | ant_pos = json_resp['antenna']['position'] |
104 - | ant_position = np.asarray([ant_pos['x'], ant_pos['y'], ant_pos['z']], |
105 - | dtype=float) |
106 - | pad_pos = json_resp['pad']['position'] |
107 - | pad_position = np.asarray([pad_pos['x'], pad_pos['y'], pad_pos['z']], |
108 - | dtype=float) |
109 - | pos_found = True |
110 - | else: |
111 - | casalog.post('Did not find position parameters for antenna {0}. ' |
112 - | 'Status code: {1}. Response: {2}'. |
113 - | format(ant_name, resp.status_code, resp.text), 'WARN') |
114 - | ant_position = np.array([0, 0, 0], dtype=float) |
115 - | pad_position = np.array([0, 0, 0], dtype=float) |
116 - | |
117 - | return (pos_found, ant_position, pad_position) |
118 - | |
119 - | casalog.post('Got these responses from ALMA TMC DB: {}'. |
120 - | format(responses), 'DEBUG2') |
121 - | accum_ant_names = [] |
122 - | accum_positions = [] |
123 - | accum_pad_positions = [] |
124 - | cnt_pos_found = 0 |
125 - | for aname, resp in zip(ant_names, responses): |
126 - | accum_ant_names.append(aname) |
127 - | (found, ant_position, pad_position) = get_ant_pad_position(aname, resp) |
128 - | if found: |
129 - | cnt_pos_found += 1 |
130 - | accum_positions.append(ant_position) |
131 - | accum_pad_positions.append(pad_position) |
132 - | |
133 - | casalog.post('Queried ALMA TMC DB and found position parameters for {} antennas out of ' |
134 - | '{} requested in total '. |
135 - | format(cnt_pos_found, len(accum_ant_names)), 'INFO') |
136 - | |
137 - | return (accum_ant_names, accum_positions, accum_pad_positions) |
138 - | |
139 - | |
140 - | def query_tmcdb_antennas_soap(ant_names, timestamp): |
141 - | """ |
142 - | Retrieves information for a list of antennas, at a given timestamp, |
143 - | by querying the ALMA TMC DB AntennaPad web service. |
144 - | See service documentation: |
145 - | https://ictwiki.alma.cl/twiki/bin/view/SoftOps/TMCDBAntennaPadService |
146 - | |
147 - | TODO: deprecate and remove SOAP stuff once the auto gencal mode is validated with |
148 - | information from the TMCDB. This stuff is left here now for testing purposes - cross- |
149 - | comparison of SOAP and REST service. |
150 - | |
151 - | :param ant_names: list of antenna names as strings |
152 - | :param timestamp: timestamp specification string (in |
153 - | '2017-01-01T16:53:54' format) |
154 - | |
155 - | :returns: response as retrieved from the web service |
156 - | :raises urllib2.URLError: if network or server issues |
157 - | """ |
158 - | |
159 - | SRV_WSDL_URL = 'http://asa.alma.cl/axis2/services/TMCDBAntennaPadService?wsdl' |
160 - | |
161 - | ALMA_CONFIG_NAME = 'CURRENT.AOS' |
162 - | |
163 - | import time |
164 - | from suds.client import Client |
165 - | |
166 - | # build a string of space-separated antenna names |
167 - | try: |
168 - | ant_names_str = ' '.join(ant for ant in ant_names) |
169 - | except RuntimeError as exc: |
170 - | raise AttributeError('Error in antenna names strings: {0}. ant_names must ' |
171 - | 'be a list of strings. Got: {1}'.format(exc, ant_names)) |
172 - | config_name = ALMA_CONFIG_NAME |
173 - | casalog.post('Querying TMC DB, positions for configuration: {0}, for these ' |
174 - | 'antennas: {1}, at time: {2}'. |
175 - | format(config_name, ant_names_str, timestamp), 'INFO') |
176 - | time_start = time.time() |
177 - | |
178 - | wscli = Client(SRV_WSDL_URL) |
179 - | resp = None |
180 - | try: |
181 - | resp = wscli.service.getAntennaPositions(configurationName=config_name, |
182 - | antennaNames=ant_names_str, |
183 - | timestamp=timestamp) |
184 - | time_end = time.time() |
185 - | casalog.post('Got response from TMC DB. Took {0}s'.format(time_end - time_start)) |
186 - | except AttributeError as exc: |
187 - | import traceback |
188 - | traceback.print_exc(file=sys.stdout) |
189 - | raise RuntimeError('Got a response back from the server but it is ' |
190 - | 'wrong or could not be parsed as a valid SOAP ' |
191 - | 'response. This occurs for example when a wrong ' |
192 - | 'timestamp is given. Response object: {0}. ' |
193 - | 'Error description:'. |
194 - | format(resp, exc)) |
195 - | |
196 - | return resp |
197 - | |
198 - | |
199 - | def process_tmcdb_response_soap_for_gencal(resp): |
200 - | """ |
201 - | Takes a response from the TMC DB AntennaPad service and produces a |
202 - | list of antenna names and a list of their position correction |
203 - | parameters. |
204 - | |
205 - | :param resp: response object from web service |
206 - | |
207 - | :returns: a tuple with three elements. The tuple has |
208 - | as first element a list of antenna names as strings, and as |
209 - | second element a vector of positions (as Bx, By, Bz) for |
210 - | every antenna. The third element holds the positions of the |
211 - | corresponding pads. |
212 - | """ |
213 - | |
214 - | def check_pos(pos, ant_idx): |
215 - | """ |
216 - | Basic sanity checks on a position object |
217 - | |
218 - | :param pos: position object as returned for one antenna by the |
219 - | TMC DB AntennaPad service. |
220 - | :param ant_idx: index of the antenna |
221 - | """ |
222 - | if not pos: |
223 - | casalog.post('Empty position object received for antenna with ' |
224 - | 'index {}'.format(ant_idx), 'ERROR') |
225 - | |
226 - | try: |
227 - | pos.completion |
228 - | except AttributeError as exc: |
229 - | raise RuntimeError('Got response but with only a single ' |
230 - | 'completion object: {0}. errorList: {1}. ' |
231 - | 'Antenna index: {2}, exception: {3}'. |
232 - | format(pos, pos.errorList, ant_idx, exc)) |
233 - | |
234 - | def get_ant_pad_position(pos): |
235 - | """ |
236 - | Get the position parameters for an antenna from a position object |
237 - | |
238 - | :param pos: position object as returned for one antenna by the |
239 - | TMC DB AntennaPad service. |
240 - | |
241 - | :returns: tuple: position found, position as a list [x,y,z], pad |
242 - | position as a list [x, y, z] |
243 - | """ |
244 - | |
245 - | pos_found = False |
246 - | # when the antenna is not found |
247 - | if not pos.completion.status == 'true' or not pos.position: |
248 - | casalog.post('Did not find position parameters for antenna {0}. ' |
249 - | 'Error description: {1}'. |
250 - | format(pos.name, pos.completion.errorList), 'WARN') |
251 - | ant_position = np.array([0, 0, 0], dtype=float) |
252 - | pad_position = np.array([0, 0, 0], dtype=float) |
253 - | else: |
254 - | ant_position = np.asarray([pos.position.x, pos.position.y, |
255 - | pos.position.z], dtype=float) |
256 - | pad_position = np.asarray([pos.pad.position.x, pos.pad.position.y, |
257 - | pos.pad.position.z], dtype=float) |
258 - | pos_found = True |
259 - | |
260 - | return (pos_found, ant_position, pad_position) |
261 - | |
262 - | if not resp: |
263 - | raise RuntimeError('No response received: {}'.format(resp)) |
264 - | elif not resp.antennaPositionList or 0 == len(resp.antennaPositionList): |
265 - | raise RuntimeError('Response with no list: {}'.format(resp)) |
266 - | |
267 - | casalog.post('Got this response from ALMA TMC DB: {}'. |
268 - | format(resp), 'DEBUG2') |
269 - | |
270 - | accum_ant_names = [] |
271 - | accum_positions = [] |
272 - | accum_pad_positions = [] |
273 - | cnt_pos_found = 0 |
274 - | for ant_idx, pos in enumerate(resp.antennaPositionList): |
275 - | check_pos(pos, ant_idx) |
276 - | |
277 - | accum_ant_names.append(pos.name) |
278 - | (found, ant_position, pad_position) = get_ant_pad_position(pos) |
279 - | if found: |
280 - | cnt_pos_found += 1 |
281 - | accum_positions.append(ant_position) |
282 - | accum_pad_positions.append(pad_position) |
283 - | |
284 - | casalog.post('Queried ALMA TMC DB and found position parameters for {} antennas out of ' |
285 - | '{} requested in total '. |
286 - | format(cnt_pos_found, len(accum_ant_names)), 'INFO') |
287 - | |
288 - | return (accum_ant_names, accum_positions, accum_pad_positions) |
289 - | |
290 - | |
291 - | def correct_ant_posns_alma(vis_name, print_offsets=False): |
292 - | """ |
293 - | Implements the interface of correct_ant_posns with specific logic |
294 - | for ALMA. The parameters (third returned variable) are calculated |
295 - | from the the difference between the pad+antenna position found from |
296 - | the web service - the pad+antenna position found in the MS (ASDM). |
297 - | |
298 - | This produces a return error code as returned by correct_ant_posns() |
299 - | by capturing some common exceptions. |
300 - | """ |
301 - | |
302 - | def get_ant_pad_names_posns(vis_name): |
303 - | """ |
304 - | Find positions recorded in the MS (ASDM). |
305 - | |
306 - | :param vis_name: name of an MS |
307 - | |
308 - | :returns: a tuple: 1) list of antenna names from the MS, 2) list |
309 - | of pad/station names from the MS, 3) positions [x, y, z] of the |
310 - | antennas as recorded in the MS, 4) positions [x, y, z] of the |
311 - | pads/stations as recorded in the MS. |
312 - | """ |
313 - | tb_tool.open(vis_name + '/ANTENNA') |
314 - | ant_names = tb_tool.getcol('NAME') |
315 - | pad_names = tb_tool.getcol('STATION') |
316 - | tb_tool.close() |
317 - | |
318 - | try: |
319 - | tb_tool.open(vis_name) |
320 - | if 'ASDM_ANTENNA' not in tb_tool.keywordnames(): |
321 - | raise RuntimeError('The ANTENNA tables from the ASDM were not ' |
322 - | 'transferred to the Measurement Set') |
323 - | if 'ASDM_STATION' not in tb_tool.keywordnames(): |
324 - | raise RuntimeError('The ANTENNA and/or STATION tables from ' |
325 - | 'the ASDM were not transferred to the ' |
326 - | 'Measurement Set') |
327 - | tb_tool.close() |
328 - | |
329 - | tb_tool.open(vis_name + '/ASDM_ANTENNA') |
330 - | asdm_ant_pos = tb_tool.getcol('position') |
331 - | tb_tool.close() |
332 - | |
333 - | tb_tool.open(vis_name + '/ASDM_STATION') |
334 - | asdm_pad_pos = tb_tool.getcol('position') |
335 - | tb_tool.close() |
336 - | |
337 - | asdm_pad_pos = asdm_pad_pos[:, 0:asdm_ant_pos.shape[1]] |
338 - | except RuntimeError as exc: |
339 - | casalog.post('Could not find pad and antenna position information ' |
340 - | 'from the ASDM_ANTENNA and ASDM_STATION subtables. ' |
341 - | 'Defaulting to the POSITION column of the ANTENNA ' |
342 - | 'subtable. This means that the parameters calculated ' |
343 - | 'are most likely inaccurate. Error description: {0}'. |
344 - | format(exc), 'WARN') |
345 - | tb_tool.open(vis_name + '/ANTENNA') |
346 - | asdm_pad_pos = tb_tool.getcol('POSITION') |
347 - | tb_tool.close() |
348 - | tb_tool.open(vis_name + '/ASDM_ANTENNA') |
349 - | asdm_ant_pos = tb_tool.getcol('position') |
350 - | tb_tool.close() |
351 - | |
352 - | ant_posns_ms = map(list, zip(asdm_ant_pos[0], asdm_ant_pos[1], |
353 - | asdm_ant_pos[2])) |
354 - | pad_posns_ms = map(list, zip(asdm_pad_pos[0], asdm_pad_pos[1], |
355 - | asdm_pad_pos[2])) |
356 - | return (ant_names, pad_names, ant_posns_ms, pad_posns_ms) |
357 - | |
358 - | def get_time_range_from_obs(vis_name): |
359 - | tb_tool.open(vis_name + '/OBSERVATION') |
360 - | time_range = tb_tool.getcol('TIME_RANGE') |
361 - | tb_tool.close() |
362 - | return time_range |
363 - | |
364 - | def build_obs_time(time_range): |
365 - | """ |
366 - | Produces an observation time string, picking the middle point in time |
367 - | between start and end of observation. |
368 - | |
369 - | :returns: time specification string formatted as for example |
370 - | 2011-11-13T04:10:12 |
371 - | """ |
372 - | obs_time = (time_range[0] + time_range[1]) / 2.0 |
373 - | obs_time = ((obs_time / 86400.0) + 2400000.5 - 2440587.5) * 86400.0 |
374 - | obs_time = time.strftime('%Y-%m-%dT%H:%M:%S', time.gmtime(obs_time)) |
375 - | return obs_time |
376 - | |
377 - | def calc_ant_params_from_positions(ant_names, ant_corr_posns, pad_posns, |
378 - | ant_posns_ms, pad_posns_ms): |
379 - | """ |
380 - | Produces the antenna position correction offset parameters required by |
381 - | gencal. Uses positions from the TMCDB and the MS. |
382 - | |
383 - | :param ant_names: list of names of the antennas |
384 - | :param ant_corr_posns: antenna positions offsets from the TMCDB |
385 - | :param pad_posns: pad positions from the TMCDB |
386 - | :param ant_posns_ms: antenna position offsets recorded from the MS/ASDM |
387 - | :param pad_posns_ms: pad/station positions recorded from the MS/ASDM |
388 - | |
389 - | :returns: position correction parameters for the antennas listed in the |
390 - | first parameter |
391 - | """ |
392 - | |
393 - | def calc_param_diff(name, ant_corr_pos, pad_pos, ant_pos_ms, pad_pos_ms): |
394 - | """ |
395 - | Produce the correction parameters expected by gencal. |
396 - | |
397 - | :param name: antenna name |
398 - | :param ant_corr_pos: antenna position from the TMCDB |
399 - | :param pad_pos: pad position from the TMCDB |
400 - | :param ant_pos_ms: antenna position recorded from the MS/ASDM |
401 - | :param ant_pos_ms: pad position recorded from the MS/ASDM |
402 - | |
403 - | :returns: position correction parameters for one antenna |
404 - | (Bx, By, Bz) |
405 - | """ |
406 - | if (pad_pos == pad_pos_ms).all(): |
407 - | return calc_param_diff_same_pad_pos(name, ant_corr_pos, pad_pos, ant_pos_ms) |
408 - | else: |
409 - | return calc_param_diff_diff_pad_pos(name, ant_corr_pos, pad_pos, ant_pos_ms, |
410 - | pad_pos_ms) |
411 - | |
412 - | def calc_param_diff_same_pad_pos(name, ant_corr_pos, pad_pos, ant_pos_ms): |
413 - | """ |
414 - | Calculate correction parameters for an antenna when the pad position has not |
415 - | changed, comparing between the info that was recorded in the MS and the current |
416 - | info from the TMC DB. |
417 - | |
418 - | Subtracts A (found from the TMCDB) - B (found in the MS), where |
419 - | A is: pad position + antenna position correction/vector |
420 - | B is: antenna position in MS |
421 - | to produce the correction parameters expected by gencal |
422 - | |
423 - | :param name: antenna name |
424 - | :param ant_corr_pos: antenna position from the TMCDB |
425 - | :param pad_pos: pad position from the TMCDB |
426 - | :param ant_pos_ms: antenna position from the MS/ASDM |
427 - | :param ant_pos_ms: pad position from the MS/ASDM |
428 - | |
429 - | :returns: position correction parameters for one antenna |
430 - | (Bx, By, Bz) |
431 - | """ |
432 - | from math import sqrt, sin, cos, asin, atan2 |
433 - | |
434 - | lat = (asin(pad_pos[2] / sqrt(pad_pos[0]**2 + pad_pos[1]**2 + |
435 - | pad_pos[2]**2))) |
436 - | lon = atan2(pad_pos[1], pad_pos[0]) |
437 - | |
438 - | itrf_diff = ant_corr_pos - ant_pos_ms |
439 - | param = np.array([0, 0, 0], dtype=float) |
440 - | param[0] = (-sin(lon) * itrf_diff[0] |
441 - | - cos(lon) * sin(lat) * itrf_diff[1] |
442 - | + cos(lon) * cos(lat) * itrf_diff[2]) |
443 - | param[1] = (cos(lon) * itrf_diff[0] |
444 - | - sin(lon) * sin(lat) * itrf_diff[1] |
445 - | + sin(lon) * cos(lat) * itrf_diff[2]) |
446 - | param[2] = (cos(lat) * itrf_diff[1] + sin(lat) * itrf_diff[2]) |
447 - | |
448 - | return param |
449 - | |
450 - | def calc_param_diff_diff_pad_pos(name, ant_corr_pos, pad_pos, ant_pos_ms, |
451 - | pad_pos_ms): |
452 - | """ |
453 - | Calculate correction parameters for an antenna when the pad position has changed. |
454 - | """ |
455 - | from math import sqrt, cos, sin, asin, atan2 |
456 - | |
457 - | lat = (asin(pad_pos[2] / sqrt(pad_pos[0]**2 + pad_pos[1]**2 + pad_pos[2]**2))) |
458 - | lon = atan2(pad_pos[1], pad_pos[0]) |
459 - | pos_tot = np.array([0, 0, 0], dtype=float) |
460 - | pos_tot[0] = (ant_corr_pos[0] - sin(-lon) * pad_pos[0] - |
461 - | cos(-lon) * sin(-lat) * pad_pos[1] + |
462 - | cos(-lon) * cos(-lat) * pad_pos[2]) |
463 - | pos_tot[1] = (ant_corr_pos[1] + cos(-lon) * pad_pos[0] - |
464 - | sin(-lon) * sin(-lat) * pad_pos[1] + |
465 - | sin(-lon) * cos(-lat) * pad_pos[2]) |
466 - | pos_tot[2] = (ant_corr_pos[2] + cos(-lat) * pad_pos[1] + |
467 - | sin(-lat) * pad_pos[2]) |
468 - | |
469 - | lat_ms = (asin(pad_pos_ms[2] / sqrt(pad_pos_ms[0]**2 + pad_pos_ms[1]**2 + |
470 - | pad_pos_ms[2]**2))) |
471 - | lon_ms = atan2(pad_pos_ms[1], pad_pos_ms[0]) |
472 - | pos_tot_ms = np.array([0, 0, 0], dtype=float) |
473 - | pos_tot_ms[0] = (ant_pos_ms[0] - sin(-lon_ms) * pad_pos_ms[0] - |
474 - | cos(-lon_ms) * sin(-lat_ms) * pad_pos_ms[1] + |
475 - | cos(-lon_ms) * cos(-lat_ms) * pad_pos_ms[2]) |
476 - | pos_tot_ms[1] = (ant_pos_ms[1] + cos(-lon_ms) * pad_pos_ms[0] - |
477 - | sin(-lon_ms) * sin(-lat_ms) * pad_pos_ms[1] + |
478 - | sin(-lon_ms) * cos(-lat_ms) * pad_pos_ms[2]) |
479 - | pos_tot_ms[2] = (ant_pos_ms[2] + cos(-lat_ms) * pad_pos_ms[1] + |
480 - | sin(-lat_ms) * ant_pos_ms[2]) |
481 - | |
482 - | pos_diff = pos_tot - pos_tot_ms |
483 - | |
484 - | # Errors fixed. Not clear at this point (201712) if/when they could be |
485 - | # retrieved from the database |
486 - | pos_err = np.array([1e-10, 1e-10, 1e-10]) |
487 - | |
488 - | if (pos_err == 0).any(): |
489 - | casalog.post('Note: some errors are null for an antenna position. Error ' |
490 - | 'vector: {}'.format(pos_err), 'WARN') |
491 - | |
492 - | # Threshold fixed as the default value in analysisUtils:correctMyAntennaPositions |
493 - | thresh = 5 |
494 - | norm_ratio = sqrt(((pos_diff / pos_err)**2).sum()) |
495 - | if norm_ratio < thresh: |
496 - | casalog.post('Note: norm of position difference / errors ({}) is lower ' |
497 - | 'than threshold ({}).'.format(norm_ratio, thresh), 'WARN') |
498 - | |
499 - | # calculate parameters for gencal |
500 - | par_tot = np.array([0, 0, 0], dtype=float) |
501 - | par_tot[0] = (pad_pos[0] - sin(lon) * ant_corr_pos[0] - |
502 - | cos(lon) * sin(lat) * ant_corr_pos[1] + |
503 - | cos(lon) * cos(lat) * ant_corr_pos[2]) |
504 - | par_tot[1] = (pad_pos[1] + cos(lon) * ant_corr_pos[0] - |
505 - | sin(lon) * sin(lat) * ant_corr_pos[1] + |
506 - | sin(lon) * cos(lat) * ant_corr_pos[2]) |
507 - | par_tot[2] = (pad_pos[2] + cos(lat) * ant_corr_pos[1] + |
508 - | sin(lat) * ant_corr_pos[2]) |
509 - | |
510 - | par_tot_ms = np.array([0, 0, 0], dtype=float) |
511 - | par_tot_ms[0] = (pad_pos_ms[0] - sin(lon_ms) * ant_pos_ms[0] - |
512 - | cos(lon_ms) * sin(lat_ms) * ant_pos_ms[1] + |
513 - | cos(lon_ms) * cos(lat_ms) * ant_pos_ms[2]) |
514 - | par_tot_ms[1] = (pad_pos_ms[1] + cos(lon_ms) * ant_pos_ms[0] - |
515 - | sin(lon_ms) * sin(lat_ms) * ant_pos_ms[1] + |
516 - | sin(lon_ms) * cos(lat_ms) * ant_pos_ms[2]) |
517 - | par_tot_ms[2] = (pad_pos_ms[2] + cos(lat_ms) * ant_pos_ms[1] + |
518 - | sin(lat_ms) * ant_pos_ms[2]) |
519 - | |
520 - | pos_par_diff = par_tot - par_tot_ms |
521 - | |
522 - | correction_thresh = 2e-3 |
523 - | norm_par = np.linalg.norm(pos_par_diff) |
524 - | if norm_par >= correction_thresh: |
525 - | casalog.post('Note: the norm of the correction for antenna {} ({}) is ' |
526 - | 'larger than {}.'. |
527 - | format(name, norm_par, correction_thresh), 'WARN') |
528 - | |
529 - | return pos_par_diff |
530 - | |
531 - | ant_params = [] |
532 - | casalog.post('Antennas {0}\nPositions from TMC DB: {1},\nPositions ' |
533 - | 'found in MS: {2}'.format(ant_names, ant_corr_posns, ant_posns_ms), |
534 - | 'DEBUG1') |
535 - | |
536 - | for idx, name in enumerate(ant_names): |
537 - | if np.all(ant_corr_posns[idx] == 0) and np.all(pad_posns[idx] == 0): |
538 - | param = np.array([0, 0, 0], dtype=float) |
539 - | else: |
540 - | param = calc_param_diff(name, ant_corr_posns[idx], pad_posns[idx], |
541 - | ant_posns_ms[idx], pad_posns_ms[idx]) |
542 - | casalog.post('Antenna name: {}, pos offset: {}, pad pos: {}, pos MS: {}, ' |
543 - | 'params: {} '.format(name, ant_corr_posns[idx], pad_posns[idx], |
544 - | ant_posns_ms[idx], param), 'DEBUG2') |
545 - | ant_params.extend(param) |
546 - | |
547 - | # build a string of comma-separated antenna names |
548 - | ant_names_str = ','.join(str(ant) for ant in ant_names) |
549 - | |
550 - | return (ant_names_str, ant_params) |
551 - | |
552 - | def print_ant_params_info(ant_names, ant_params): |
553 - | """ |
554 - | Produce one line per antenna: name + 3 params, from the two values |
555 - | returned to gencal """ |
556 - | pretty_pars = 'Parameters produced by antenna:\n' |
557 - | for idx, name in enumerate(ant_names): |
558 - | idx3 = idx*3 |
559 - | pretty_pars += ('{0}: {1:14.5e} {2:14.5e} {3:14.5e}\n'. |
560 - | format(name, ant_params[idx3], ant_params[idx3+1], |
561 - | ant_params[idx3+2])) |
562 - | return pretty_pars |
563 - | |
564 - | time_range = get_time_range_from_obs(vis_name) |
565 - | ant_names, _pad_names, ant_posns_ms, pad_posns_ms = get_ant_pad_names_posns(vis_name) |
566 - | obs_time = build_obs_time(time_range) |
567 - | |
568 - | # the three element to return |
569 - | ret_code = 1 |
570 - | ant_names_str = '' |
571 - | ant_params = [] |
572 - | # Get corrected positions by querying the TMC database via the TMCDB |
573 - | # AntennaPad service |
574 - | try: |
575 - | import urllib2 |
576 - | (ant_names_db, ant_corr_posns, pad_posns) = _fetch_tmcdb_info(ant_names, obs_time) |
577 - | if (ant_names_db != ant_names).any(): |
578 - | raise RuntimeError('The antenna names found in the MS (which were ' |
579 - | 'used to query the TMC DB) do not match the ' |
580 - | 'names returned by the database.\nFound in MS: ' |
581 - | '{0}.\nFound in TMC DB: {1}'. |
582 - | format(ant_names_db, ant_names)) |
583 - | ant_names_str, ant_params = ( |
584 - | calc_ant_params_from_positions(ant_names, ant_corr_posns, |
585 - | pad_posns, ant_posns_ms, pad_posns_ms)) |
586 - | ret_code = 0 |
587 - | except urllib2.URLError as exc: |
588 - | casalog.post('Network or server issue found while querying ALMA TMC DB ' |
589 - | 'AntennaPad service. Details: {}'.format(exc), 'ERROR') |
590 - | ret_code = 2 |
591 - | except RuntimeError as exc: |
592 - | casalog.post('Issue found while querying ALMA TMC DB AntennaPad ' |
593 - | 'service. Details: {}'.format(exc), 'ERROR') |
594 - | ret_code = 1 |
595 - | |
596 - | casalog.post(print_ant_params_info(ant_names, ant_params), 'DEBUG1') |
597 - | ant_names = ant_names_str.split(',') |
598 - | format_pars = '[' + ', '.join(['{:.5e}'.format(val) for val in ant_params]) + ']' |
599 - | casalog.post('Parameter values (FPARAM) produced for gencal, using position information ' |
600 - | 'retrieved from the ALMA TMC DB:\n{0}'.format(format_pars), 'INFO') |
601 - | |
602 - | return [ret_code, ant_names_str, ant_params] |