1 /*
2 * Copyright (c) 2016, The University of Oxford
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * 3. Neither the name of the University of Oxford nor the names of its
13 * contributors may be used to endorse or promote products derived from this
14 * software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <Python.h>
30
31 /* http://docs.scipy.org/doc/numpy-dev/reference/c-api.deprecations.html */
32 #define NPY_NO_DEPRECATED_API NPY_1_8_API_VERSION
33 #include <numpy/arrayobject.h>
34 #include <stdio.h>
35 #include <math.h>
36 #include <string.h>
37 #include <stddef.h>
38
39 #ifndef M_PI
40 #define M_PI 3.14159265358979323846264338327950288
41 #endif
42
43 typedef struct DComplex {
44 double re;
45 double im;
46 } DComplex;
47
48 typedef struct Complex {
49 float re;
50 float im;
51 } Complex;
52
53
54 /**
55 * @brief
56 * @details
57 */
apply_gains(PyObject * self,PyObject * args)58 static PyObject* apply_gains(PyObject* self, PyObject* args)
59 {
60 /* Read input arguments */
61 PyObject *vis_in_ = 0, *gains_ = 0;
62 PyObject *pyo_vis_in = 0, *pyo_vis_out = 0, *pyo_gains = 0;
63 if (!PyArg_ParseTuple(args, "OO", &vis_in_, &gains_))
64 return 0;
65
66 /* Return an ndarray from the python objects */
67 pyo_vis_in = PyArray_FROM_OTF(vis_in_, NPY_CDOUBLE, NPY_ARRAY_IN_ARRAY);
68 pyo_gains = PyArray_FROM_OTF(gains_, NPY_CDOUBLE, NPY_ARRAY_IN_ARRAY);
69 if (!pyo_vis_in || !pyo_gains) goto fail;
70
71 /* TODO(BM) Error checking on the dims! */
72 int nd_vis = PyArray_NDIM((PyArrayObject*)pyo_vis_in);
73 npy_intp* vis_dims = PyArray_DIMS((PyArrayObject*)pyo_vis_in);
74 /*int num_vis = (int) vis_dims[0];*/
75
76 /*int nd_gains = PyArray_NDIM((PyArrayObject*)pyo_gains);*/
77 npy_intp* gains_dims = PyArray_DIMS((PyArrayObject*)pyo_gains);
78 int num_antennas = (int) gains_dims[0];
79
80 /* Create PyObject for output visibilities */
81 pyo_vis_out = PyArray_SimpleNew(nd_vis, vis_dims, NPY_CDOUBLE);
82
83 /* Apply the gains: v_out = gp * v_in * conj(gq) */
84 DComplex* v_out = (DComplex*)PyArray_DATA((PyArrayObject*)pyo_vis_out);
85 DComplex* g = (DComplex*)PyArray_DATA((PyArrayObject*)pyo_gains);
86 DComplex* v_in = (DComplex*)PyArray_DATA((PyArrayObject*)pyo_vis_in);
87 for (int i = 0, p = 0; p < num_antennas; ++p) {
88 for (int q = p + 1; q < num_antennas; ++q, ++i) {
89 double a = v_in[i].re * g[p].re - v_in[i].im * g[p].im;
90 double b = v_in[i].im * g[p].re + v_in[i].re * g[p].im;
91 v_out[i].re = a * g[q].re + b * g[q].im;
92 v_out[i].im = b * g[q].re - a * g[q].im;
93 }
94 }
95 /* Decrement references to temporary array objects. */
96 Py_XDECREF(pyo_vis_in);
97 Py_XDECREF(pyo_gains);
98 return Py_BuildValue("N", pyo_vis_out);
99
100 /*
101 printf(" - Ref count: %zi, %zi, %zi\n",
102 PyArray_REFCOUNT(pyo_vis_in),
103 PyArray_REFCOUNT(pyo_gains),
104 PyArray_REFCOUNT(pyo_vis_out));
105 */
106
107 fail:
108 Py_XDECREF(pyo_gains);
109 Py_XDECREF(pyo_vis_in);
110 Py_XDECREF(pyo_vis_out);
111 return 0;
112 }
113
114
115 /**
116 * @brief
117 * @details
118 */
apply_gains_2(PyObject * self,PyObject * args)119 static PyObject* apply_gains_2(PyObject* self, PyObject* args)
120 {
121 /* Read input arguments */
122 PyObject *vis_in_o = 0, *gains_o = 0;
123 PyObject *pyo_vis_in = 0, *pyo_vis_out = 0, *pyo_gains = 0;
124 if (!PyArg_ParseTuple(args, "OO", &vis_in_o, &gains_o))
125 return 0;
126
127 /* Return an ndarray from the python objects */
128 pyo_vis_in = PyArray_FROM_OTF(vis_in_o, NPY_CFLOAT, NPY_ARRAY_IN_ARRAY);
129 pyo_gains = PyArray_FROM_OTF(gains_o, NPY_CFLOAT, NPY_ARRAY_IN_ARRAY);
130 if (!pyo_vis_in || !pyo_gains) goto fail;
131
132 /* TODO(BM) Error checking on the dims! */
133 int nd_vis = PyArray_NDIM((PyArrayObject*)pyo_vis_in);
134 npy_intp* vis_dims = PyArray_DIMS((PyArrayObject*)pyo_vis_in);
135 /*int num_vis = (int) vis_dims[0];*/
136
137 /*int nd_gains = PyArray_NDIM((PyArrayObject*)pyo_gains);*/
138 npy_intp* gains_dims = PyArray_DIMS((PyArrayObject*)pyo_gains);
139 int num_antennas = (int) gains_dims[0];
140
141 /* Create PyObject for output visibilities */
142 pyo_vis_out = PyArray_SimpleNew(nd_vis, vis_dims, NPY_CFLOAT);
143
144 /* Apply the gains: v_out = gp * v_in * conj(gq) */
145 Complex* v_out = (Complex*)PyArray_DATA((PyArrayObject*)pyo_vis_out);
146 Complex* g = (Complex*)PyArray_DATA((PyArrayObject*)pyo_gains);
147 Complex* v_in = (Complex*)PyArray_DATA((PyArrayObject*)pyo_vis_in);
148 for (int i = 0, p = 0; p < num_antennas; ++p) {
149 for (int q = p + 1; q < num_antennas; ++q, ++i) {
150 float a = v_in[i].re * g[p].re - v_in[i].im * g[p].im;
151 float b = v_in[i].im * g[p].re + v_in[i].re * g[p].im;
152 v_out[i].re = a * g[q].re + b * g[q].im;
153 v_out[i].im = b * g[q].re - a * g[q].im;
154 }
155 }
156 /* Decrement references to temporary array objects. */
157 Py_XDECREF(pyo_vis_in);
158 Py_XDECREF(pyo_gains);
159 return Py_BuildValue("N", pyo_vis_out);
160
161 fail:
162 Py_XDECREF(pyo_gains);
163 Py_XDECREF(pyo_vis_in);
164 Py_XDECREF(pyo_vis_out);
165 return 0;
166 }
167
168
169
170 /**
171 * @brief
172 * @details
173 */
vis_list_to_matrix(PyObject * self,PyObject * args)174 static PyObject* vis_list_to_matrix(PyObject* self, PyObject* args)
175 {
176 /* Read input arguments */
177 PyObject* vis_list_ = NULL;
178 int num_ant = 0;
179 if (!PyArg_ParseTuple(args, "Oi", &vis_list_, &num_ant))
180 return NULL;
181
182 /* Convert to an ndarray */
183 PyObject* pyo_vis_list = PyArray_FROM_OTF(vis_list_, NPY_CDOUBLE,
184 NPY_ARRAY_IN_ARRAY);
185 if (!pyo_vis_list) goto fail;
186
187 /* TODO(BM) Error checking on the dims!
188 int nd_vis = PyArray_NDIM((PyArrayObject*)pyo_vis_list);
189 npy_intp* vis_dims = PyArray_DIMS((PyArrayObject*)pyo_vis_list);
190 int num_vis = vis_dims[0];*/
191
192 /* Create PyObject for output visibilities */
193 npy_intp dims[] = { num_ant, num_ant };
194 PyObject* pyo_vis_matrix = PyArray_SimpleNew(2, dims, NPY_CDOUBLE);
195
196 DComplex* v_list = (DComplex*)PyArray_DATA((PyArrayObject*)pyo_vis_list);
197 DComplex* v_matrix = (DComplex*)PyArray_DATA((PyArrayObject*)pyo_vis_matrix);
198 for (int i = 0, p = 0; p < num_ant; ++p) {
199 for (int q = p + 1; q < num_ant; ++q, ++i) {
200 v_matrix[p * num_ant + q].re = v_list[i].re;
201 v_matrix[p * num_ant + q].im = -v_list[i].im;
202 v_matrix[q * num_ant + p].re = v_list[i].re;
203 v_matrix[q * num_ant + p].im = v_list[i].im;
204 }
205 }
206 for (int i = 0; i < num_ant; ++i) {
207 v_matrix[i * num_ant + i].re = 0.0;
208 v_matrix[i * num_ant + i].im = 0.0;
209 }
210
211 /* Decrement references to temporary array objects. */
212 Py_XDECREF(pyo_vis_list);
213 return Py_BuildValue("N", pyo_vis_matrix);
214
215 fail:
216 Py_XDECREF(pyo_vis_list);
217 return 0;
218 }
219
220
221 /**
222 * @brief
223 * @details
224 */
vis_list_to_matrix_2(PyObject * self,PyObject * args)225 static PyObject* vis_list_to_matrix_2(PyObject* self, PyObject* args)
226 {
227 /* Read input arguments */
228 PyObject* vis_list_ = NULL;
229 int num_ant = 0;
230 if (!PyArg_ParseTuple(args, "Oi", &vis_list_, &num_ant))
231 return NULL;
232
233 /* Convert to an ndarray */
234 PyObject* pyo_vis_list = PyArray_FROM_OTF(vis_list_, NPY_CFLOAT,
235 NPY_ARRAY_IN_ARRAY);
236 if (!pyo_vis_list) goto fail;
237
238 /* TODO(BM) Error checking on the dims!
239 int nd_vis = PyArray_NDIM((PyArrayObject*)pyo_vis_list);
240 npy_intp* vis_dims = PyArray_DIMS((PyArrayObject*)pyo_vis_list);
241 int num_vis = vis_dims[0];*/
242
243 /* Create PyObject for output visibilities */
244 npy_intp dims[] = { num_ant, num_ant };
245 PyObject* pyo_vis_matrix = PyArray_SimpleNew(2, dims, NPY_CFLOAT);
246
247 Complex* v_list = (Complex*)PyArray_DATA((PyArrayObject*)pyo_vis_list);
248 Complex* v_matrix = (Complex*)PyArray_DATA((PyArrayObject*)pyo_vis_matrix);
249 for (int i = 0, p = 0; p < num_ant; ++p) {
250 for (int q = p + 1; q < num_ant; ++q, ++i) {
251 v_matrix[p * num_ant + q].re = v_list[i].re;
252 v_matrix[p * num_ant + q].im = -v_list[i].im;
253 v_matrix[q * num_ant + p].re = v_list[i].re;
254 v_matrix[q * num_ant + p].im = v_list[i].im;
255 }
256 }
257 for (int i = 0; i < num_ant; ++i) {
258 v_matrix[i * num_ant + i].re = 0.0;
259 v_matrix[i * num_ant + i].im = 0.0;
260 }
261
262 /* Decrement references to temporary array objects. */
263 Py_XDECREF(pyo_vis_list);
264 return Py_BuildValue("N", pyo_vis_matrix);
265
266 fail:
267 Py_XDECREF(pyo_vis_list);
268 return 0;
269 }
270
271
272
check_ref_count(PyObject * self,PyObject * args)273 static PyObject* check_ref_count(PyObject* self, PyObject* args)
274 {
275 PyObject* obj = NULL;
276 /* https://docs.python.org/2/c-api/arg.html */
277 /* Reference count is not increased by 'O' for PyArg_ParseTyple */
278 if (!PyArg_ParseTuple(args, "O", &obj))
279 return NULL;
280 return Py_BuildValue("ii", PyArray_REFCOUNT(obj), Py_REFCNT(obj));
281 }
282
283
expand(PyObject * self,PyObject * args)284 static PyObject* expand(PyObject* self, PyObject* args)
285 {
286 PyObject *vis_in_comp = 0, *vis_out_orig = 0;
287 PyArrayObject *ant1_, *ant2_, *weight_, *data_in_, *data_out_;
288 int num_antennas = 0, num_baselines = 0, num_input_vis = 0;
289 int *ant1, *ant2, a1, a2, b, t, row_in, row_out, *out_time_idx = 0, w;
290 double *data_in, *data_out, *weight, d[2];
291 const char *input_name, *output_name;
292 if (!PyArg_ParseTuple(args, "iOsOs", &num_antennas,
293 &vis_in_comp, &input_name, &vis_out_orig, &output_name))
294 return 0;
295 num_baselines = num_antennas * (num_antennas - 1) / 2;
296 ant1_ = (PyArrayObject*)PyDict_GetItemString(vis_in_comp, "antenna1");
297 ant2_ = (PyArrayObject*)PyDict_GetItemString(vis_in_comp, "antenna2");
298 weight_ = (PyArrayObject*)PyDict_GetItemString(vis_in_comp, "weight");
299 data_in_ = (PyArrayObject*)PyDict_GetItemString(vis_in_comp, input_name);
300 data_out_ = (PyArrayObject*)PyDict_GetItemString(vis_out_orig, output_name);
301 num_input_vis = (int)*PyArray_DIMS(data_in_);
302 ant1 = (int*) PyArray_DATA(ant1_);
303 ant2 = (int*) PyArray_DATA(ant2_);
304 weight = (double*) PyArray_DATA(weight_);
305 data_in = (double*) PyArray_DATA(data_in_);
306 data_out = (double*) PyArray_DATA(data_out_);
307 out_time_idx = (int*) calloc(num_baselines, sizeof(int));
308
309 for (row_in = 0; row_in < num_input_vis; ++row_in)
310 {
311 a1 = ant1[row_in];
312 a2 = ant2[row_in];
313 w = (int) round(weight[row_in]);
314 d[0] = data_in[2*row_in + 0];
315 d[1] = data_in[2*row_in + 1];
316 b = a1 * (num_antennas - 1) - (a1 - 1) * a1 / 2 + a2 - a1 - 1;
317 for (t = out_time_idx[b]; t < out_time_idx[b] + w; ++t)
318 {
319 row_out = t * num_baselines + b;
320 data_out[2*row_out + 0] = d[0];
321 data_out[2*row_out + 1] = d[1];
322 }
323 out_time_idx[b] += w;
324 }
325
326 free(out_time_idx);
327 return Py_BuildValue("");
328 }
329
330 struct oskar_BDA
331 {
332 /* Options. */
333 int num_antennas, num_baselines, num_pols, num_times;
334 double duvw_max, dt_max, max_fact, fov_deg, delta_t;
335
336 /* Output data. */
337 int bda_row, output_size, *ant1, *ant2;
338 double *u, *v, *w, *data, *time, *exposure, *sigma, *weight;
339
340 /* Current UVW coordinates. */
341 double *uu_current, *vv_current, *ww_current;
342
343 /* Buffers of deltas along the baseline in the current average. */
344 double *duvw, *dt;
345
346 /* Buffers of the current average along the baseline. */
347 double *ave_uu, *ave_vv, *ave_ww, *ave_data;
348 int *ave_count;
349 };
350 typedef struct oskar_BDA oskar_BDA;
351
352 static const char* name = "oskar_BDA";
353
354 #define INT sizeof(int)
355 #define DBL sizeof(double)
356
oskar_bda_clear(oskar_BDA * h)357 static void oskar_bda_clear(oskar_BDA* h)
358 {
359 /* Clear averages. */
360 memset(h->uu_current, '\0', h->num_baselines * DBL);
361 memset(h->vv_current, '\0', h->num_baselines * DBL);
362 memset(h->ww_current, '\0', h->num_baselines * DBL);
363 memset(h->duvw, '\0', h->num_baselines * DBL);
364 memset(h->dt, '\0', h->num_baselines * DBL);
365 memset(h->ave_uu, '\0', h->num_baselines * DBL);
366 memset(h->ave_vv, '\0', h->num_baselines * DBL);
367 memset(h->ave_ww, '\0', h->num_baselines * DBL);
368 memset(h->ave_count, '\0', h->num_baselines * INT);
369 memset(h->ave_data, '\0', h->num_baselines * h->num_pols * 2*DBL);
370
371 /* Free the output data. */
372 h->output_size = 0;
373 h->bda_row = 0;
374 free(h->ant1);
375 free(h->ant2);
376 free(h->u);
377 free(h->v);
378 free(h->w);
379 free(h->data);
380 free(h->time);
381 free(h->exposure);
382 free(h->sigma);
383 free(h->weight);
384 h->ant1 = 0;
385 h->ant2 = 0;
386 h->u = 0;
387 h->v = 0;
388 h->w = 0;
389 h->data = 0;
390 h->time = 0;
391 h->exposure = 0;
392 h->sigma = 0;
393 h->weight = 0;
394 }
395
396
oskar_bda_free(oskar_BDA * h)397 static void oskar_bda_free(oskar_BDA* h)
398 {
399 oskar_bda_clear(h);
400 free(h->uu_current);
401 free(h->vv_current);
402 free(h->ww_current);
403 free(h->duvw);
404 free(h->dt);
405 free(h->ave_uu);
406 free(h->ave_vv);
407 free(h->ave_ww);
408 free(h->ave_data);
409 free(h->ave_count);
410 free(h);
411 }
412
413
414 /* arcsinc(x) function from Obit. Uses Newton-Raphson method. */
inv_sinc(double value)415 static double inv_sinc(double value)
416 {
417 double x1 = 0.001;
418 for (int i = 0; i < 1000; ++i)
419 {
420 double x0 = x1;
421 double a = x0 * M_PI;
422 x1 = x0 - ((sin(a) / a) - value) /
423 ((a * cos(a) - M_PI * sin(a)) / (a * a));
424 if (fabs(x1 - x0) < 1.0e-6)
425 break;
426 }
427 return x1;
428 }
429
430
bda_free(PyObject * capsule)431 static void bda_free(PyObject* capsule)
432 {
433 oskar_BDA* h = (oskar_BDA*) PyCapsule_GetPointer(capsule, name);
434 oskar_bda_free(h);
435 }
436
437
get_handle(PyObject * capsule)438 static oskar_BDA* get_handle(PyObject* capsule)
439 {
440 oskar_BDA* h = 0;
441 if (!PyCapsule_CheckExact(capsule))
442 {
443 PyErr_SetString(PyExc_RuntimeError, "Object is not a PyCapsule.");
444 return 0;
445 }
446 h = (oskar_BDA*) PyCapsule_GetPointer(capsule, name);
447 if (!h)
448 {
449 PyErr_SetString(PyExc_RuntimeError,
450 "Capsule is not of type oskar_BDA.");
451 return 0;
452 }
453 return h;
454 }
455
456
bda_create(PyObject * self,PyObject * args)457 static PyObject* bda_create(PyObject* self, PyObject* args)
458 {
459 oskar_BDA* h = 0;
460 PyObject* capsule = 0;
461 int num_antennas, num_baselines, num_pols;
462 if (!PyArg_ParseTuple(args, "ii", &num_antennas, &num_pols))
463 return 0;
464
465 /* Create and initialise the BDA object. */
466 h = (oskar_BDA*) calloc(1, sizeof(oskar_BDA));
467 num_baselines = num_antennas * (num_antennas - 1) / 2;
468 h->num_antennas = num_antennas;
469 h->num_baselines = num_baselines;
470 h->num_pols = num_pols;
471 h->uu_current = (double*) calloc(num_baselines, DBL);
472 h->vv_current = (double*) calloc(num_baselines, DBL);
473 h->ww_current = (double*) calloc(num_baselines, DBL);
474 h->duvw = (double*) calloc(num_baselines, DBL);
475 h->dt = (double*) calloc(num_baselines, DBL);
476 h->ave_uu = (double*) calloc(num_baselines, DBL);
477 h->ave_vv = (double*) calloc(num_baselines, DBL);
478 h->ave_ww = (double*) calloc(num_baselines, DBL);
479 h->ave_count = (int*) calloc(num_baselines, INT);
480 h->ave_data = (double*) calloc(num_baselines * num_pols, 2*DBL);
481
482 /* Initialise and return the PyCapsule. */
483 capsule = PyCapsule_New((void*)h, name, (PyCapsule_Destructor)bda_free);
484 return Py_BuildValue("N", capsule); /* Don't increment refcount. */
485 }
486
487
bda_set_compression(PyObject * self,PyObject * args)488 static PyObject* bda_set_compression(PyObject* self, PyObject* args)
489 {
490 oskar_BDA* h = 0;
491 PyObject* capsule = 0;
492 double max_fact = 0.0, fov_deg = 0.0, wavelength = 0.0, dt_max = 0.0;
493 if (!PyArg_ParseTuple(args, "Odddd",
494 &capsule, &max_fact, &fov_deg, &wavelength, &dt_max))
495 return 0;
496 if (!(h = get_handle(capsule))) return 0;
497 h->max_fact = max_fact;
498 h->fov_deg = fov_deg;
499 h->duvw_max = inv_sinc(1.0 / max_fact) / (fov_deg * (M_PI / 180.0));
500 h->duvw_max *= wavelength;
501 h->dt_max = dt_max;
502 return Py_BuildValue("d", h->duvw_max);
503 }
504
505
bda_set_delta_t(PyObject * self,PyObject * args)506 static PyObject* bda_set_delta_t(PyObject* self, PyObject* args)
507 {
508 oskar_BDA* h = 0;
509 PyObject* capsule = 0;
510 double delta_t = 0.0;
511 if (!PyArg_ParseTuple(args, "Od", &capsule, &delta_t)) return 0;
512 if (!(h = get_handle(capsule))) return 0;
513 h->delta_t = delta_t;
514 return Py_BuildValue("");
515 }
516
517
bda_set_num_times(PyObject * self,PyObject * args)518 static PyObject* bda_set_num_times(PyObject* self, PyObject* args)
519 {
520 oskar_BDA* h = 0;
521 PyObject* capsule = 0;
522 int num_times = 0;
523 if (!PyArg_ParseTuple(args, "Oi", &capsule, &num_times)) return 0;
524 if (!(h = get_handle(capsule))) return 0;
525 h->num_times = num_times;
526 return Py_BuildValue("");
527 }
528
529
bda_set_initial_coords(PyObject * self,PyObject * args)530 static PyObject* bda_set_initial_coords(PyObject* self, PyObject* args)
531 {
532 oskar_BDA* h = 0;
533 PyObject *obj[] = {0, 0, 0, 0};
534 PyArrayObject *uu_ = 0, *vv_ = 0, *ww_ = 0;
535 if (!PyArg_ParseTuple(args, "OOOO",
536 &obj[0], &obj[1], &obj[2], &obj[3]))
537 return 0;
538 if (!(h = get_handle(obj[0]))) return 0;
539
540 /* Make sure input objects are arrays. Convert if required. */
541 uu_ = (PyArrayObject*) PyArray_FROM_OTF(obj[1],
542 NPY_DOUBLE, NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST);
543 if (!uu_) goto fail;
544 vv_ = (PyArrayObject*) PyArray_FROM_OTF(obj[2],
545 NPY_DOUBLE, NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST);
546 if (!vv_) goto fail;
547 ww_ = (PyArrayObject*) PyArray_FROM_OTF(obj[3],
548 NPY_DOUBLE, NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST);
549 if (!ww_) goto fail;
550
551 /* Check dimensions. */
552 if (PyArray_NDIM(uu_) != 1 || PyArray_NDIM(vv_) != 1 ||
553 PyArray_NDIM(ww_) != 1)
554 {
555 PyErr_SetString(PyExc_RuntimeError, "Coordinate arrays must be 1D.");
556 goto fail;
557 }
558 if (h->num_baselines != (int)*PyArray_DIMS(uu_) ||
559 h->num_baselines != (int)*PyArray_DIMS(vv_) ||
560 h->num_baselines != (int)*PyArray_DIMS(ww_))
561 {
562 PyErr_SetString(PyExc_RuntimeError, "Input data dimension mismatch.");
563 goto fail;
564 }
565
566 /* Copy the data. */
567 memcpy(h->uu_current, PyArray_DATA(uu_), h->num_baselines * DBL);
568 memcpy(h->vv_current, PyArray_DATA(vv_), h->num_baselines * DBL);
569 memcpy(h->ww_current, PyArray_DATA(ww_), h->num_baselines * DBL);
570
571 Py_XDECREF(uu_);
572 Py_XDECREF(vv_);
573 Py_XDECREF(ww_);
574 return Py_BuildValue("");
575
576 fail:
577 Py_XDECREF(uu_);
578 Py_XDECREF(vv_);
579 Py_XDECREF(ww_);
580 return 0;
581 }
582
583
bda_add_data(PyObject * self,PyObject * args)584 static PyObject* bda_add_data(PyObject* self, PyObject* args)
585 {
586 oskar_BDA* h = 0;
587 PyObject *obj[] = {0, 0, 0, 0, 0};
588 PyArrayObject *amp_current_ = 0;
589 PyArrayObject *uu_next_ = 0, *vv_next_ = 0, *ww_next_ = 0;
590 int a1, a2, b, i, j, p, t = 0;
591 double *uu_next = 0, *vv_next = 0, *ww_next = 0, *data;
592 if (!PyArg_ParseTuple(args, "OiOOOO", &obj[0], &t, &obj[1],
593 &obj[2], &obj[3], &obj[4])) return 0;
594 if (!(h = get_handle(obj[0]))) return 0;
595
596 /* Get array handles. */
597 amp_current_ = (PyArrayObject*) PyArray_FROM_OTF(obj[1],
598 NPY_CDOUBLE, NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST);
599 if (!amp_current_) goto fail;
600 if ((obj[2] != Py_None) && (obj[3] != Py_None) && (obj[4] != Py_None))
601 {
602 /* Make sure input objects are arrays. Convert if required. */
603 uu_next_ = (PyArrayObject*) PyArray_FROM_OTF(obj[2],
604 NPY_DOUBLE, NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST);
605 if (!uu_next_) goto fail;
606 vv_next_ = (PyArrayObject*) PyArray_FROM_OTF(obj[3],
607 NPY_DOUBLE, NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST);
608 if (!vv_next_) goto fail;
609 ww_next_ = (PyArrayObject*) PyArray_FROM_OTF(obj[4],
610 NPY_DOUBLE, NPY_ARRAY_IN_ARRAY | NPY_ARRAY_FORCECAST);
611 if (!ww_next_) goto fail;
612
613 /* Check dimensions. */
614 if (PyArray_NDIM(uu_next_) != 1 ||
615 PyArray_NDIM(vv_next_) != 1 ||
616 PyArray_NDIM(ww_next_) != 1)
617 {
618 PyErr_SetString(PyExc_RuntimeError,
619 "Coordinate arrays must be 1D.");
620 goto fail;
621 }
622 if (h->num_baselines != (int)*PyArray_DIMS(uu_next_) ||
623 h->num_baselines != (int)*PyArray_DIMS(vv_next_) ||
624 h->num_baselines != (int)*PyArray_DIMS(ww_next_))
625 {
626 PyErr_SetString(PyExc_RuntimeError,
627 "Input data dimension mismatch.");
628 goto fail;
629 }
630 }
631
632 if (uu_next_) uu_next = (double*) PyArray_DATA(uu_next_);
633 if (vv_next_) vv_next = (double*) PyArray_DATA(vv_next_);
634 if (ww_next_) ww_next = (double*) PyArray_DATA(ww_next_);
635 data = (double*) PyArray_DATA(amp_current_);
636 for (a1 = 0, b = 0; a1 < h->num_antennas; ++a1)
637 {
638 for (a2 = a1 + 1; a2 < h->num_antennas; ++a2, ++b)
639 {
640 double du, dv, dw, b_duvw = 0.0, s;
641
642 /* Accumulate into averages. */
643 h->ave_count[b] += 1;
644 h->ave_uu[b] += h->uu_current[b];
645 h->ave_vv[b] += h->vv_current[b];
646 h->ave_ww[b] += h->ww_current[b];
647 for (p = 0; p < h->num_pols; ++p)
648 {
649 i = b * h->num_pols + p;
650 h->ave_data[2*i + 0] += data[2*i + 0];
651 h->ave_data[2*i + 1] += data[2*i + 1];
652 }
653
654 /* Look ahead to the next point on the baseline and see if
655 * this is also in the average. */
656 if ((t < h->num_times - 1) && uu_next && vv_next && ww_next)
657 {
658 du = uu_next[b] - h->uu_current[b];
659 dv = vv_next[b] - h->vv_current[b];
660 dw = ww_next[b] - h->ww_current[b];
661 b_duvw = sqrt(du*du + dv*dv + dw*dw);
662 }
663
664 /* If last time or if next point extends beyond average,
665 * save out averaged data and reset,
666 * else accumulate current average lengths. */
667 if (t == h->num_times - 1 ||
668 h->duvw[b] + b_duvw > h->duvw_max ||
669 h->dt[b] + h->delta_t > h->dt_max)
670 {
671 s = 1.0 / h->ave_count[b];
672 h->ave_uu[b] *= s;
673 h->ave_vv[b] *= s;
674 h->ave_ww[b] *= s;
675 for (p = 0; p < h->num_pols; ++p)
676 {
677 i = b * h->num_pols + p;
678 h->ave_data[2*i + 0] *= s;
679 h->ave_data[2*i + 1] *= s;
680 }
681
682 /* Store the averaged data. */
683 if (h->output_size < h->bda_row + 1)
684 {
685 h->output_size += h->num_baselines;
686 h->ant1 = realloc(h->ant1, h->output_size * INT);
687 h->ant2 = realloc(h->ant2, h->output_size * INT);
688 h->u = realloc(h->u, h->output_size * DBL);
689 h->v = realloc(h->v, h->output_size * DBL);
690 h->w = realloc(h->w, h->output_size * DBL);
691 h->exposure = realloc(h->exposure, h->output_size * DBL);
692 h->sigma = realloc(h->sigma, h->output_size * DBL);
693 h->weight = realloc(h->weight, h->output_size * DBL);
694 h->data = realloc(h->data,
695 h->num_pols * h->output_size * 2*DBL);
696 }
697 h->ant1[h->bda_row] = a1;
698 h->ant2[h->bda_row] = a2;
699 h->u[h->bda_row] = h->ave_uu[b];
700 h->v[h->bda_row] = h->ave_vv[b];
701 h->w[h->bda_row] = h->ave_ww[b];
702 h->exposure[h->bda_row] = h->ave_count[b] * h->delta_t;
703 h->sigma[h->bda_row] = sqrt(s);
704 h->weight[h->bda_row] = h->ave_count[b];
705 for (p = 0; p < h->num_pols; ++p)
706 {
707 i = b * h->num_pols + p;
708 j = h->bda_row * h->num_pols + p;
709 h->data[2*j + 0] = h->ave_data[2*i + 0];
710 h->data[2*j + 1] = h->ave_data[2*i + 1];
711 }
712
713 /* Reset baseline accumulation buffers. */
714 h->duvw[b] = 0.0;
715 h->dt[b] = 0.0;
716 h->ave_count[b] = 0;
717 h->ave_uu[b] = 0.0;
718 h->ave_vv[b] = 0.0;
719 h->ave_ww[b] = 0.0;
720 for (p = 0; p < h->num_pols; ++p)
721 {
722 i = b * h->num_pols + p;
723 h->ave_data[2*i + 0] = 0.0;
724 h->ave_data[2*i + 1] = 0.0;
725 }
726
727 /* Update baseline average row counter for next output. */
728 h->bda_row += 1;
729 }
730 else
731 {
732 /* Accumulate distance and time on current baseline. */
733 h->duvw[b] += b_duvw;
734 h->dt[b] += h->delta_t;
735 }
736 }
737 } /* end baseline loop */
738
739 /* Copy "next" to "current" for the next time block. */
740 if (uu_next) memcpy(h->uu_current, uu_next, h->num_baselines * DBL);
741 if (vv_next) memcpy(h->vv_current, vv_next, h->num_baselines * DBL);
742 if (ww_next) memcpy(h->ww_current, ww_next, h->num_baselines * DBL);
743
744 Py_XDECREF(amp_current_);
745 Py_XDECREF(uu_next_);
746 Py_XDECREF(vv_next_);
747 Py_XDECREF(ww_next_);
748 return Py_BuildValue("");
749
750 fail:
751 Py_XDECREF(amp_current_);
752 Py_XDECREF(uu_next_);
753 Py_XDECREF(vv_next_);
754 Py_XDECREF(ww_next_);
755 return 0;
756 }
757
758
bda_finalise(PyObject * self,PyObject * args)759 static PyObject* bda_finalise(PyObject* self, PyObject* args)
760 {
761 oskar_BDA* h = 0;
762 PyObject* capsule = 0, *dict;
763 PyArrayObject *ant1, *ant2, *uu, *vv, *ww, *data;
764 PyArrayObject *expo, *sigma, *weight;
765 if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
766 if (!(h = get_handle(capsule))) return 0;
767
768 /* Create arrays that will be returned to Python. */
769 npy_intp dims1[] = {h->bda_row};
770 npy_intp dims2[] = {h->bda_row * h->num_pols};
771 ant1 = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_INT);
772 ant2 = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_INT);
773 uu = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_DOUBLE);
774 vv = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_DOUBLE);
775 ww = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_DOUBLE);
776 data = (PyArrayObject*)PyArray_SimpleNew(1, dims2, NPY_CDOUBLE);
777 expo = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_DOUBLE);
778 sigma = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_DOUBLE);
779 weight = (PyArrayObject*)PyArray_SimpleNew(1, dims1, NPY_DOUBLE);
780
781 /* Copy the data into the arrays. */
782 memcpy(PyArray_DATA(ant1), h->ant1, h->bda_row * INT);
783 memcpy(PyArray_DATA(ant2), h->ant2, h->bda_row * INT);
784 memcpy(PyArray_DATA(uu), h->u, h->bda_row * DBL);
785 memcpy(PyArray_DATA(vv), h->v, h->bda_row * DBL);
786 memcpy(PyArray_DATA(ww), h->w, h->bda_row * DBL);
787 memcpy(PyArray_DATA(data), h->data, h->bda_row * h->num_pols * 2*DBL);
788 memcpy(PyArray_DATA(expo), h->exposure, h->bda_row * DBL);
789 memcpy(PyArray_DATA(sigma), h->sigma, h->bda_row * DBL);
790 memcpy(PyArray_DATA(weight), h->weight, h->bda_row * DBL);
791
792 /* Create a dictionary and return the data in it. */
793 dict = PyDict_New();
794 PyDict_SetItemString(dict, "antenna1", (PyObject*)ant1);
795 PyDict_SetItemString(dict, "antenna2", (PyObject*)ant2);
796 PyDict_SetItemString(dict, "uu", (PyObject*)uu);
797 PyDict_SetItemString(dict, "vv", (PyObject*)vv);
798 PyDict_SetItemString(dict, "ww", (PyObject*)ww);
799 PyDict_SetItemString(dict, "data", (PyObject*)data);
800 PyDict_SetItemString(dict, "exposure", (PyObject*)expo);
801 PyDict_SetItemString(dict, "sigma", (PyObject*)sigma);
802 PyDict_SetItemString(dict, "weight", (PyObject*)weight);
803
804 /* Clear all current averages. */
805 oskar_bda_clear(h);
806
807 return Py_BuildValue("N", dict); /* Don't increment refcount. */
808 }
809
810
811 /* Method table. */
812 static PyMethodDef methods[] =
813 {
814 {
815 "apply_gains",
816 (PyCFunction)apply_gains, METH_VARARGS,
817 "vis_amp = apply_gains(vis_amp, gains)\n"
818 "Applies gains.\n"
819 },
820 {
821 "apply_gains_2",
822 (PyCFunction)apply_gains_2, METH_VARARGS,
823 "vis_amp = apply_gains_2(vis_amp, gains)\n"
824 "Applies gains.\n"
825 },
826 {
827 "vis_list_to_matrix",
828 (PyCFunction)vis_list_to_matrix, METH_VARARGS,
829 "vis_matrix = vis_list_to_matrix(vis_list)\n"
830 "Converts a list of visibilities to matrix form.\n"
831 },
832 {
833 "vis_list_to_matrix_2",
834 (PyCFunction)vis_list_to_matrix_2, METH_VARARGS,
835 "vis_matrix = vis_list_to_matrix_2(vis_list)\n"
836 "Converts a list of visibilities to matrix form.\n"
837 },
838 {
839 "check_ref_count",
840 (PyCFunction)check_ref_count, METH_VARARGS,
841 "count = check_ref_count(PyObject)\n"
842 "Check the reference count of a python object\n"
843 },
844 {
845 "expand",
846 (PyCFunction)expand, METH_VARARGS,
847 "expand(num_antennas)\n"
848 "Expands BDA data.\n"
849 },
850 {
851 "bda_create",
852 (PyCFunction)bda_create, METH_VARARGS,
853 "handle = bda_create(num_antennas, num_pols)\n"
854 "Creates the BDA object.\n"
855 },
856 {
857 "bda_set_compression",
858 (PyCFunction)bda_set_compression, METH_VARARGS,
859 "bda_set_max_fact(max_fact, fov_deg, wavelength_m, max_avg_time_s)\n"
860 "Sets compression parameters.\n"
861 },
862 {
863 "bda_set_delta_t",
864 (PyCFunction)bda_set_delta_t, METH_VARARGS,
865 "bda_set_delta_t(value)\n"
866 "Sets time interval of input data.\n"
867 },
868 {
869 "bda_set_num_times",
870 (PyCFunction)bda_set_num_times, METH_VARARGS,
871 "bda_set_num_times(value)\n"
872 "Sets number of times in the input data.\n"
873 },
874 {
875 "bda_set_initial_coords",
876 (PyCFunction)bda_set_initial_coords, METH_VARARGS,
877 "bda_set_initial_coords(uu, vv, ww)\n"
878 "Sets initial baseline coordinates.\n"
879 },
880 {
881 "bda_add_data",
882 (PyCFunction)bda_add_data, METH_VARARGS,
883 "bda_add_data(time_index, vis, uu_next, vv_next, ww_next)\n"
884 "Supplies visibility data to be averaged.\n"
885 },
886 {
887 "bda_finalise",
888 (PyCFunction)bda_finalise, METH_VARARGS,
889 "averaged_data = bda_finalise()\n"
890 "Returns averaged visibility data as a dictionary of arrays.\n"
891 },
892 {NULL, NULL, 0, NULL}
893 };
894
895 #if PY_MAJOR_VERSION >= 3
896 static PyModuleDef moduledef = {
897 PyModuleDef_HEAD_INIT,
898 "_bda_utils", /* m_name */
899 NULL, /* m_doc */
900 -1, /* m_size */
901 methods /* m_methods */
902 };
903 #endif
904
905
moduleinit(void)906 static PyObject* moduleinit(void)
907 {
908 PyObject* m;
909 #if PY_MAJOR_VERSION >= 3
910 m = PyModule_Create(&moduledef);
911 #else
912 m = Py_InitModule3("_bda_utils", methods, "docstring ...");
913 #endif
914 return m;
915 }
916
917 #if PY_MAJOR_VERSION >= 3
PyInit__bda_utils(void)918 PyMODINIT_FUNC PyInit__bda_utils(void)
919 {
920 import_array();
921 return moduleinit();
922 }
923 #else
924 // XXX the init function name has to match that of the compiled module
925 // with the pattern 'init<module name>'. This module is called '_apply_gains'
init_bda_utils(void)926 void init_bda_utils(void)
927 {
928 import_array();
929 moduleinit();
930 return;
931 }
932 #endif
933