1 /*
2  * Copyright (c) 2016-2021, The OSKAR Developers.
3  * See the LICENSE file at the top-level directory of this distribution.
4  */
5 
6 #include <Python.h>
7 
8 #include <oskar.h>
9 #include <oskar_version.h>
10 #include <string.h>
11 
12 /* http://docs.scipy.org/doc/numpy-dev/reference/c-api.deprecations.html */
13 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
14 #include <numpy/arrayobject.h>
15 
16 static const char module_doc[] =
17         "This module provides an interface to the OSKAR telescope model.";
18 static const char name[] = "oskar_Telescope";
19 
get_handle(PyObject * capsule,const char * name)20 static void* get_handle(PyObject* capsule, const char* name)
21 {
22     void* h = 0;
23     if (!PyCapsule_CheckExact(capsule))
24     {
25         PyErr_SetString(PyExc_RuntimeError, "Object is not a PyCapsule.");
26         return 0;
27     }
28     if (!(h = PyCapsule_GetPointer(capsule, name)))
29     {
30         PyErr_Format(PyExc_RuntimeError, "Capsule is not of type %s.", name);
31         return 0;
32     }
33     return h;
34 }
35 
36 
telescope_free(PyObject * capsule)37 static void telescope_free(PyObject* capsule)
38 {
39     int status = 0;
40     oskar_telescope_free((oskar_Telescope*) get_handle(capsule, name), &status);
41 }
42 
43 
capsule_name(PyObject * self,PyObject * args)44 static PyObject* capsule_name(PyObject* self, PyObject* args)
45 {
46     PyObject *capsule = 0;
47     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
48     if (!PyCapsule_CheckExact(capsule))
49     {
50         PyErr_SetString(PyExc_RuntimeError, "Object is not a PyCapsule.");
51         return 0;
52     }
53     return Py_BuildValue("s", PyCapsule_GetName(capsule));
54 }
55 
56 
create(PyObject * self,PyObject * args)57 static PyObject* create(PyObject* self, PyObject* args)
58 {
59     oskar_Telescope* h = 0;
60     PyObject* capsule = 0;
61     int status = 0, prec = 0;
62     const char* type;
63     if (!PyArg_ParseTuple(args, "s", &type)) return 0;
64     prec = (type[0] == 'S' || type[0] == 's') ? OSKAR_SINGLE : OSKAR_DOUBLE;
65     h = oskar_telescope_create(prec, OSKAR_CPU, 0, &status);
66     capsule = PyCapsule_New((void*)h, name,
67             (PyCapsule_Destructor)telescope_free);
68     return Py_BuildValue("N", capsule); /* Don't increment refcount. */
69 }
70 
71 
load(PyObject * self,PyObject * args)72 static PyObject* load(PyObject* self, PyObject* args)
73 {
74     oskar_Telescope* h = 0;
75     PyObject* capsule = 0;
76     int status = 0;
77     const char* dir_name;
78     if (!PyArg_ParseTuple(args, "Os", &capsule, &dir_name)) return 0;
79     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
80     oskar_telescope_load(h, dir_name, 0, &status);
81 
82     /* Check for errors. */
83     if (status)
84     {
85         PyErr_Format(PyExc_RuntimeError,
86                 "oskar_telescope_load() failed with code %d (%s).",
87                 status, oskar_get_error_string(status));
88         return 0;
89     }
90     return Py_BuildValue("");
91 }
92 
93 
identical_stations(PyObject * self,PyObject * args)94 static PyObject* identical_stations(PyObject* self, PyObject* args)
95 {
96     PyErr_Format(PyExc_RuntimeError,
97             "This method is no longer available.");
98     return 0;
99 }
100 
101 
max_station_depth(PyObject * self,PyObject * args)102 static PyObject* max_station_depth(PyObject* self, PyObject* args)
103 {
104     oskar_Telescope* h = 0;
105     PyObject* capsule = 0;
106     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
107     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
108     return Py_BuildValue("i", oskar_telescope_max_station_depth(h));
109 }
110 
111 
max_station_size(PyObject * self,PyObject * args)112 static PyObject* max_station_size(PyObject* self, PyObject* args)
113 {
114     oskar_Telescope* h = 0;
115     PyObject* capsule = 0;
116     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
117     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
118     return Py_BuildValue("i", oskar_telescope_max_station_size(h));
119 }
120 
121 
num_baselines(PyObject * self,PyObject * args)122 static PyObject* num_baselines(PyObject* self, PyObject* args)
123 {
124     oskar_Telescope* h = 0;
125     PyObject* capsule = 0;
126     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
127     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
128     return Py_BuildValue("i", oskar_telescope_num_baselines(h));
129 }
130 
131 
num_stations(PyObject * self,PyObject * args)132 static PyObject* num_stations(PyObject* self, PyObject* args)
133 {
134     oskar_Telescope* h = 0;
135     PyObject* capsule = 0;
136     if (!PyArg_ParseTuple(args, "O", &capsule)) return 0;
137     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
138     return Py_BuildValue("i", oskar_telescope_num_stations(h));
139 }
140 
141 
override_element_cable_length_errors(PyObject * self,PyObject * args)142 static PyObject* override_element_cable_length_errors(
143         PyObject* self, PyObject* args)
144 {
145     oskar_Telescope* h = 0;
146     PyObject* capsule = 0;
147     int feed = 0, seed = 0, status = 0;
148     double mean = 0.0, std = 0.0;
149     if (!PyArg_ParseTuple(args, "Oiidd", &capsule, &feed, &seed, &mean, &std))
150         return 0;
151     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
152 #if OSKAR_VERSION >= 0x020800
153     oskar_telescope_override_element_cable_length_errors(h, feed, seed,
154             mean, std, &status);
155     if (status)
156     {
157         PyErr_Format(PyExc_RuntimeError,
158                 "oskar_telescope_override_element_cable_length_errors() failed "
159                 "with code %d (%s).", status, oskar_get_error_string(status));
160         return 0;
161     }
162     return Py_BuildValue("");
163 #elif OSKAR_VERSION > 0x020701
164     oskar_telescope_override_element_cable_length_errors(h, seed,
165             mean, std, &status);
166     if (status)
167     {
168         PyErr_Format(PyExc_RuntimeError,
169                 "oskar_telescope_override_element_cable_length_errors() failed "
170                 "with code %d (%s).", status, oskar_get_error_string(status));
171         return 0;
172     }
173     return Py_BuildValue("");
174 #else
175     (void) h;
176     (void) status;
177     PyErr_SetString(PyExc_RuntimeError,
178             "This function is not available in OSKAR " OSKAR_VERSION_STR ". "
179             "Please update to a newer version.");
180     return 0;
181 #endif
182 }
183 
184 
override_element_gains(PyObject * self,PyObject * args)185 static PyObject* override_element_gains(PyObject* self, PyObject* args)
186 {
187     oskar_Telescope* h = 0;
188     PyObject* capsule = 0;
189     int feed = 0, seed = 0, status = 0;
190     double mean = 0.0, std = 0.0;
191     if (!PyArg_ParseTuple(args, "Oiidd", &capsule, &feed, &seed, &mean, &std))
192         return 0;
193     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
194 #if OSKAR_VERSION >= 0x020800
195     oskar_telescope_override_element_gains(h, feed, seed, mean, std, &status);
196     if (status)
197     {
198         PyErr_Format(PyExc_RuntimeError,
199                 "oskar_telescope_override_element_gains() failed "
200                 "with code %d (%s).", status, oskar_get_error_string(status));
201         return 0;
202     }
203     return Py_BuildValue("");
204 #elif OSKAR_VERSION > 0x020701
205     oskar_telescope_override_element_gains(h, seed, mean, std, &status);
206     if (status)
207     {
208         PyErr_Format(PyExc_RuntimeError,
209                 "oskar_telescope_override_element_gains() failed "
210                 "with code %d (%s).", status, oskar_get_error_string(status));
211         return 0;
212     }
213     return Py_BuildValue("");
214 #else
215     (void) h;
216     (void) status;
217     PyErr_SetString(PyExc_RuntimeError,
218             "This function is not available in OSKAR " OSKAR_VERSION_STR ". "
219             "Please update to a newer version.");
220     return 0;
221 #endif
222 }
223 
224 
override_element_phases(PyObject * self,PyObject * args)225 static PyObject* override_element_phases(PyObject* self, PyObject* args)
226 {
227     oskar_Telescope* h = 0;
228     PyObject* capsule = 0;
229     int feed = 0, seed = 0, status = 0;
230     double std_rad = 0.0;
231     if (!PyArg_ParseTuple(args, "Oiid", &capsule, &feed, &seed, &std_rad))
232         return 0;
233     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
234 #if OSKAR_VERSION >= 0x020800
235     oskar_telescope_override_element_phases(h, feed, seed, std_rad, &status);
236     if (status)
237     {
238         PyErr_Format(PyExc_RuntimeError,
239                 "oskar_telescope_override_element_phases() failed "
240                 "with code %d (%s).", status, oskar_get_error_string(status));
241         return 0;
242     }
243     return Py_BuildValue("");
244 #elif OSKAR_VERSION > 0x020701
245     oskar_telescope_override_element_phases(h, seed, std_rad, &status);
246     if (status)
247     {
248         PyErr_Format(PyExc_RuntimeError,
249                 "oskar_telescope_override_element_phases() failed "
250                 "with code %d (%s).", status, oskar_get_error_string(status));
251         return 0;
252     }
253     return Py_BuildValue("");
254 #else
255     (void) h;
256     (void) status;
257     PyErr_SetString(PyExc_RuntimeError,
258             "This function is not available in OSKAR " OSKAR_VERSION_STR ". "
259             "Please update to a newer version.");
260     return 0;
261 #endif
262 }
263 
264 
set_allow_station_beam_duplication(PyObject * self,PyObject * args)265 static PyObject* set_allow_station_beam_duplication(PyObject* self,
266         PyObject* args)
267 {
268     oskar_Telescope* h = 0;
269     PyObject* capsule = 0;
270     int value = 0;
271     if (!PyArg_ParseTuple(args, "Oi", &capsule, &value)) return 0;
272     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
273     oskar_telescope_set_allow_station_beam_duplication(h, value);
274     return Py_BuildValue("");
275 }
276 
277 
set_channel_bandwidth(PyObject * self,PyObject * args)278 static PyObject* set_channel_bandwidth(PyObject* self, PyObject* args)
279 {
280     oskar_Telescope* h = 0;
281     PyObject* capsule = 0;
282     double channel_bandwidth_hz = 0.0;
283     if (!PyArg_ParseTuple(args, "Od", &capsule,
284             &channel_bandwidth_hz)) return 0;
285     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
286     oskar_telescope_set_channel_bandwidth(h, channel_bandwidth_hz);
287     return Py_BuildValue("");
288 }
289 
290 
set_enable_noise(PyObject * self,PyObject * args)291 static PyObject* set_enable_noise(PyObject* self, PyObject* args)
292 {
293     oskar_Telescope* h = 0;
294     PyObject* capsule = 0;
295     int value = 0, seed = 0;
296     if (!PyArg_ParseTuple(args, "Oii", &capsule, &value, &seed)) return 0;
297     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
298     oskar_telescope_set_enable_noise(h, value, (unsigned int) seed);
299     return Py_BuildValue("");
300 }
301 
302 
set_enable_numerical_patterns(PyObject * self,PyObject * args)303 static PyObject* set_enable_numerical_patterns(PyObject* self, PyObject* args)
304 {
305     oskar_Telescope* h = 0;
306     PyObject* capsule = 0;
307     int value = 0;
308     if (!PyArg_ParseTuple(args, "Oi", &capsule, &value)) return 0;
309     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
310     oskar_telescope_set_enable_numerical_patterns(h, value);
311     return Py_BuildValue("");
312 }
313 
314 
set_gaussian_station_beam_width(PyObject * self,PyObject * args)315 static PyObject* set_gaussian_station_beam_width(PyObject* self,
316         PyObject* args)
317 {
318     oskar_Telescope* h = 0;
319     PyObject* capsule = 0;
320     double fwhm_deg = 0.0, ref_freq_hz = 0.0;
321     if (!PyArg_ParseTuple(args, "Odd", &capsule, &fwhm_deg, &ref_freq_hz))
322         return 0;
323     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
324 
325     /* Check stations exist. */
326     if (oskar_telescope_num_stations(h) == 0)
327     {
328         PyErr_Format(PyExc_RuntimeError, "No stations in telescope model!");
329         return 0;
330     }
331     oskar_telescope_set_gaussian_station_beam_width(h, fwhm_deg, ref_freq_hz);
332     return Py_BuildValue("");
333 }
334 
335 
set_noise_freq(PyObject * self,PyObject * args)336 static PyObject* set_noise_freq(PyObject* self, PyObject* args)
337 {
338     oskar_Telescope* h = 0;
339     PyObject* capsule = 0;
340     int num_channels = 0, status = 0;
341     double start_hz = 0.0, inc_hz = 0.0;
342     if (!PyArg_ParseTuple(args, "Oddi", &capsule, &start_hz, &inc_hz,
343             &num_channels)) return 0;
344     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
345 
346     /* Check stations exist. */
347     if (oskar_telescope_num_stations(h) == 0)
348     {
349         PyErr_Format(PyExc_RuntimeError, "No stations in telescope model!");
350         return 0;
351     }
352     oskar_telescope_set_noise_freq(h, start_hz, inc_hz, num_channels, &status);
353 
354     /* Check for errors. */
355     if (status)
356     {
357         PyErr_Format(PyExc_RuntimeError,
358                 "oskar_telescope_set_noise_freq() failed with code %d (%s).",
359                 status, oskar_get_error_string(status));
360         return 0;
361     }
362     return Py_BuildValue("");
363 }
364 
365 
set_noise_rms(PyObject * self,PyObject * args)366 static PyObject* set_noise_rms(PyObject* self, PyObject* args)
367 {
368     oskar_Telescope* h = 0;
369     PyObject* capsule = 0;
370     int status = 0;
371     double start = 0.0, end = 0.0;
372     if (!PyArg_ParseTuple(args, "Odd", &capsule, &start, &end)) return 0;
373     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
374 
375     /* Check stations exist. */
376     if (oskar_telescope_num_stations(h) == 0)
377     {
378         PyErr_Format(PyExc_RuntimeError, "No stations in telescope model!");
379         return 0;
380     }
381     oskar_telescope_set_noise_rms(h, start, end, &status);
382 
383     /* Check for errors. */
384     if (status)
385     {
386         PyErr_Format(PyExc_RuntimeError,
387                 "oskar_telescope_set_noise_rms() failed "
388                 "with code %d (%s).\n\n"
389                 "Remember to set noise frequencies first!",
390                 status, oskar_get_error_string(status));
391         return 0;
392     }
393     return Py_BuildValue("");
394 }
395 
396 
set_phase_centre(PyObject * self,PyObject * args)397 static PyObject* set_phase_centre(PyObject* self, PyObject* args)
398 {
399     oskar_Telescope* h = 0;
400     PyObject* capsule = 0;
401     double ra_rad = 0.0, dec_rad = 0.0;
402     if (!PyArg_ParseTuple(args, "Odd", &capsule, &ra_rad, &dec_rad)) return 0;
403     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
404 
405     /* Check stations exist. */
406     if (oskar_telescope_num_stations(h) == 0)
407     {
408         PyErr_Format(PyExc_RuntimeError, "No stations in telescope model!");
409         return 0;
410     }
411 #if OSKAR_VERSION >= 0x020800
412     oskar_telescope_set_phase_centre(h, OSKAR_COORDS_RADEC,
413             ra_rad, dec_rad);
414 #else
415     oskar_telescope_set_phase_centre(h, OSKAR_SPHERICAL_TYPE_EQUATORIAL,
416             ra_rad, dec_rad);
417 #endif
418     return Py_BuildValue("");
419 }
420 
421 
set_pol_mode(PyObject * self,PyObject * args)422 static PyObject* set_pol_mode(PyObject* self, PyObject* args)
423 {
424     oskar_Telescope* h = 0;
425     PyObject* capsule = 0;
426     int status = 0;
427     const char* mode;
428     if (!PyArg_ParseTuple(args, "Os", &capsule, &mode)) return 0;
429     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
430     oskar_telescope_set_pol_mode(h, mode, &status);
431     if (status)
432     {
433         PyErr_SetString(PyExc_RuntimeError, "Unknown polarisation mode.");
434         return 0;
435     }
436     return Py_BuildValue("");
437 }
438 
439 
set_position(PyObject * self,PyObject * args)440 static PyObject* set_position(PyObject* self, PyObject* args)
441 {
442     oskar_Telescope* h = 0;
443     PyObject* capsule = 0;
444     double longitude_rad = 0.0, latitude_rad = 0.0, altitude_m = 0.0;
445     if (!PyArg_ParseTuple(args, "Oddd", &capsule,
446             &longitude_rad, &latitude_rad, &altitude_m)) return 0;
447     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
448     oskar_telescope_set_position(h, longitude_rad, latitude_rad, altitude_m);
449     return Py_BuildValue("");
450 }
451 
452 
set_station_coords_ecef(PyObject * self,PyObject * args)453 static PyObject* set_station_coords_ecef(PyObject* self, PyObject* args)
454 {
455     oskar_Telescope* h = 0;
456     PyObject *obj[] = {0, 0, 0, 0, 0, 0, 0};
457     oskar_Mem *x_c, *y_c, *z_c, *x_err_c, *y_err_c, *z_err_c;
458     PyArrayObject *x = 0, *y = 0, *z = 0, *x_err = 0, *y_err = 0, *z_err = 0;
459     int status = 0, flags, i, num_stations;
460     double longitude, latitude, altitude;
461 
462     /* Parse inputs. */
463     if (!PyArg_ParseTuple(args, "OdddOOOOOO", &obj[0],
464             &longitude, &latitude, &altitude,
465             &obj[1], &obj[2], &obj[3], &obj[4], &obj[5], &obj[6])) return 0;
466     if (!(h = (oskar_Telescope*) get_handle(obj[0], name))) return 0;
467 
468     /* Make sure input objects are arrays. Convert if required. */
469     flags = NPY_ARRAY_FORCECAST | NPY_ARRAY_IN_ARRAY;
470     x     = (PyArrayObject*) PyArray_FROM_OTF(obj[1], NPY_DOUBLE, flags);
471     y     = (PyArrayObject*) PyArray_FROM_OTF(obj[2], NPY_DOUBLE, flags);
472     z     = (PyArrayObject*) PyArray_FROM_OTF(obj[3], NPY_DOUBLE, flags);
473     x_err = (PyArrayObject*) PyArray_FROM_OTF(obj[4], NPY_DOUBLE, flags);
474     y_err = (PyArrayObject*) PyArray_FROM_OTF(obj[5], NPY_DOUBLE, flags);
475     z_err = (PyArrayObject*) PyArray_FROM_OTF(obj[6], NPY_DOUBLE, flags);
476     if (!x || !y || !z || !x_err || !y_err || !z_err)
477         goto fail;
478 
479     /* Check size of input arrays. */
480     num_stations = (int) PyArray_SIZE(x);
481     if (num_stations != (int) PyArray_SIZE(y) ||
482             num_stations != (int) PyArray_SIZE(z) ||
483             num_stations != (int) PyArray_SIZE(x_err) ||
484             num_stations != (int) PyArray_SIZE(y_err) ||
485             num_stations != (int) PyArray_SIZE(z_err))
486     {
487         PyErr_SetString(PyExc_RuntimeError, "Input data dimension mismatch.");
488         goto fail;
489     }
490 
491     /* Pointers to input arrays. */
492     x_c = oskar_mem_create_alias_from_raw(PyArray_DATA(x),
493             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
494     y_c = oskar_mem_create_alias_from_raw(PyArray_DATA(y),
495             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
496     z_c = oskar_mem_create_alias_from_raw(PyArray_DATA(z),
497             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
498     x_err_c = oskar_mem_create_alias_from_raw(PyArray_DATA(x_err),
499             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
500     y_err_c = oskar_mem_create_alias_from_raw(PyArray_DATA(y_err),
501             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
502     z_err_c = oskar_mem_create_alias_from_raw(PyArray_DATA(z_err),
503             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
504 
505     /* Set data. */
506 #if OSKAR_VERSION >= 0x020800
507     oskar_telescope_set_station_coords_ecef(h, longitude, latitude, altitude,
508             num_stations, x_c, y_c, z_c, x_err_c, y_err_c, z_err_c, &status);
509     oskar_telescope_resize_station_array(h, 1, &status);
510     for (i = 0; i < 1; ++i)
511     {
512         oskar_Station* station = oskar_telescope_station(h, i);
513         oskar_station_resize(station, 1, &status);
514         oskar_station_resize_element_types(station, 1, &status);
515     }
516     oskar_telescope_set_station_ids_and_coords(h, &status);
517 #else
518     oskar_telescope_set_station_coords_ecef(h, longitude, latitude, altitude,
519             num_stations, x_c, y_c, z_c, x_err_c, y_err_c, z_err_c, &status);
520     for (i = 0; i < num_stations; ++i)
521     {
522         oskar_Station* station = oskar_telescope_station(h, i);
523         oskar_station_resize(station, 1, &status);
524         oskar_station_resize_element_types(station, 1, &status);
525     }
526 #endif
527 
528     /* Free memory. */
529     oskar_mem_free(x_c, &status);
530     oskar_mem_free(y_c, &status);
531     oskar_mem_free(z_c, &status);
532     oskar_mem_free(x_err_c, &status);
533     oskar_mem_free(y_err_c, &status);
534     oskar_mem_free(z_err_c, &status);
535 
536     Py_XDECREF(x);
537     Py_XDECREF(y);
538     Py_XDECREF(z);
539     Py_XDECREF(x_err);
540     Py_XDECREF(y_err);
541     Py_XDECREF(z_err);
542     return Py_BuildValue("");
543 
544 fail:
545     Py_XDECREF(x);
546     Py_XDECREF(y);
547     Py_XDECREF(z);
548     Py_XDECREF(x_err);
549     Py_XDECREF(y_err);
550     Py_XDECREF(z_err);
551     return 0;
552 }
553 
554 
set_station_coords_enu(PyObject * self,PyObject * args)555 static PyObject* set_station_coords_enu(PyObject* self, PyObject* args)
556 {
557     oskar_Telescope* h = 0;
558     PyObject *obj[] = {0, 0, 0, 0, 0, 0, 0};
559     oskar_Mem *x_c, *y_c, *z_c, *x_err_c, *y_err_c, *z_err_c;
560     PyArrayObject *x = 0, *y = 0, *z = 0, *x_err = 0, *y_err = 0, *z_err = 0;
561     int status = 0, flags, i, num_stations;
562     double longitude, latitude, altitude;
563 
564     /* Parse inputs. */
565     if (!PyArg_ParseTuple(args, "OdddOOOOOO", &obj[0],
566             &longitude, &latitude, &altitude,
567             &obj[1], &obj[2], &obj[3], &obj[4], &obj[5], &obj[6])) return 0;
568     if (!(h = (oskar_Telescope*) get_handle(obj[0], name))) return 0;
569 
570     /* Make sure input objects are arrays. Convert if required. */
571     flags = NPY_ARRAY_FORCECAST | NPY_ARRAY_IN_ARRAY;
572     x     = (PyArrayObject*) PyArray_FROM_OTF(obj[1], NPY_DOUBLE, flags);
573     y     = (PyArrayObject*) PyArray_FROM_OTF(obj[2], NPY_DOUBLE, flags);
574     z     = (PyArrayObject*) PyArray_FROM_OTF(obj[3], NPY_DOUBLE, flags);
575     x_err = (PyArrayObject*) PyArray_FROM_OTF(obj[4], NPY_DOUBLE, flags);
576     y_err = (PyArrayObject*) PyArray_FROM_OTF(obj[5], NPY_DOUBLE, flags);
577     z_err = (PyArrayObject*) PyArray_FROM_OTF(obj[6], NPY_DOUBLE, flags);
578     if (!x || !y || !z || !x_err || !y_err || !z_err)
579         goto fail;
580 
581     /* Check size of input arrays. */
582     num_stations = (int) PyArray_SIZE(x);
583     if (num_stations != (int) PyArray_SIZE(y) ||
584             num_stations != (int) PyArray_SIZE(z) ||
585             num_stations != (int) PyArray_SIZE(x_err) ||
586             num_stations != (int) PyArray_SIZE(y_err) ||
587             num_stations != (int) PyArray_SIZE(z_err))
588     {
589         PyErr_SetString(PyExc_RuntimeError, "Input data dimension mismatch.");
590         goto fail;
591     }
592 
593     /* Pointers to input arrays. */
594     x_c = oskar_mem_create_alias_from_raw(PyArray_DATA(x),
595             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
596     y_c = oskar_mem_create_alias_from_raw(PyArray_DATA(y),
597             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
598     z_c = oskar_mem_create_alias_from_raw(PyArray_DATA(z),
599             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
600     x_err_c = oskar_mem_create_alias_from_raw(PyArray_DATA(x_err),
601             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
602     y_err_c = oskar_mem_create_alias_from_raw(PyArray_DATA(y_err),
603             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
604     z_err_c = oskar_mem_create_alias_from_raw(PyArray_DATA(z_err),
605             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
606 
607     /* Set data. */
608 #if OSKAR_VERSION >= 0x020800
609     oskar_telescope_set_station_coords_enu(h, longitude, latitude, altitude,
610             num_stations, x_c, y_c, z_c, x_err_c, y_err_c, z_err_c, &status);
611     oskar_telescope_resize_station_array(h, 1, &status);
612     for (i = 0; i < 1; ++i)
613     {
614         oskar_Station* station = oskar_telescope_station(h, i);
615         oskar_station_resize(station, 1, &status);
616         oskar_station_resize_element_types(station, 1, &status);
617     }
618     oskar_telescope_set_station_ids_and_coords(h, &status);
619 #else
620     oskar_telescope_set_station_coords_enu(h, longitude, latitude, altitude,
621             num_stations, x_c, y_c, z_c, x_err_c, y_err_c, z_err_c, &status);
622     for (i = 0; i < num_stations; ++i)
623     {
624         oskar_Station* station = oskar_telescope_station(h, i);
625         oskar_station_resize(station, 1, &status);
626         oskar_station_resize_element_types(station, 1, &status);
627     }
628 #endif
629 
630     /* Free memory. */
631     oskar_mem_free(x_c, &status);
632     oskar_mem_free(y_c, &status);
633     oskar_mem_free(z_c, &status);
634     oskar_mem_free(x_err_c, &status);
635     oskar_mem_free(y_err_c, &status);
636     oskar_mem_free(z_err_c, &status);
637 
638     Py_XDECREF(x);
639     Py_XDECREF(y);
640     Py_XDECREF(z);
641     Py_XDECREF(x_err);
642     Py_XDECREF(y_err);
643     Py_XDECREF(z_err);
644     return Py_BuildValue("");
645 
646 fail:
647     Py_XDECREF(x);
648     Py_XDECREF(y);
649     Py_XDECREF(z);
650     Py_XDECREF(x_err);
651     Py_XDECREF(y_err);
652     Py_XDECREF(z_err);
653     return 0;
654 }
655 
656 
set_station_coords_wgs84(PyObject * self,PyObject * args)657 static PyObject* set_station_coords_wgs84(PyObject* self, PyObject* args)
658 {
659     oskar_Telescope* h = 0;
660     PyObject *obj[] = {0, 0, 0, 0};
661     oskar_Mem *lon_deg_c, *lat_deg_c, *alt_m_c;
662     PyArrayObject *lon_deg = 0, *lat_deg = 0, *alt_m = 0;
663     int status = 0, flags, i, num_stations;
664     double longitude, latitude, altitude;
665 
666     /* Parse inputs. */
667     if (!PyArg_ParseTuple(args, "OdddOOO", &obj[0],
668             &longitude, &latitude, &altitude,
669             &obj[1], &obj[2], &obj[3])) return 0;
670     if (!(h = (oskar_Telescope*) get_handle(obj[0], name))) return 0;
671 
672     /* Make sure input objects are arrays. Convert if required. */
673     flags = NPY_ARRAY_FORCECAST | NPY_ARRAY_IN_ARRAY;
674     lon_deg = (PyArrayObject*) PyArray_FROM_OTF(obj[1], NPY_DOUBLE, flags);
675     lat_deg = (PyArrayObject*) PyArray_FROM_OTF(obj[2], NPY_DOUBLE, flags);
676     alt_m   = (PyArrayObject*) PyArray_FROM_OTF(obj[3], NPY_DOUBLE, flags);
677     if (!lon_deg || !lat_deg || !alt_m)
678         goto fail;
679 
680     /* Check size of input arrays. */
681     num_stations = (int) PyArray_SIZE(lon_deg);
682     if (num_stations != (int) PyArray_SIZE(lat_deg) ||
683             num_stations != (int) PyArray_SIZE(alt_m))
684     {
685         PyErr_SetString(PyExc_RuntimeError, "Input data dimension mismatch.");
686         goto fail;
687     }
688 
689     /* Pointers to input arrays. */
690     lon_deg_c = oskar_mem_create_alias_from_raw(PyArray_DATA(lon_deg),
691             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
692     lat_deg_c = oskar_mem_create_alias_from_raw(PyArray_DATA(lat_deg),
693             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
694     alt_m_c = oskar_mem_create_alias_from_raw(PyArray_DATA(alt_m),
695             OSKAR_DOUBLE, OSKAR_CPU, num_stations, &status);
696 
697     /* Set data. */
698 #if OSKAR_VERSION >= 0x020800
699     oskar_telescope_set_station_coords_wgs84(h, longitude, latitude, altitude,
700             num_stations, lon_deg_c, lat_deg_c, alt_m_c, &status);
701     oskar_telescope_resize_station_array(h, 1, &status);
702     for (i = 0; i < 1; ++i)
703     {
704         oskar_Station* station = oskar_telescope_station(h, i);
705         oskar_station_resize(station, 1, &status);
706         oskar_station_resize_element_types(station, 1, &status);
707     }
708     oskar_telescope_set_station_ids_and_coords(h, &status);
709 #else
710     oskar_telescope_set_station_coords_wgs84(h, longitude, latitude, altitude,
711             num_stations, lon_deg_c, lat_deg_c, alt_m_c, &status);
712     for (i = 0; i < num_stations; ++i)
713     {
714         oskar_Station* station = oskar_telescope_station(h, i);
715         oskar_station_resize(station, 1, &status);
716         oskar_station_resize_element_types(station, 1, &status);
717     }
718 #endif
719 
720     /* Free memory. */
721     oskar_mem_free(lon_deg_c, &status);
722     oskar_mem_free(lat_deg_c, &status);
723     oskar_mem_free(alt_m_c, &status);
724 
725     Py_XDECREF(lon_deg);
726     Py_XDECREF(lat_deg);
727     Py_XDECREF(alt_m);
728     return Py_BuildValue("");
729 
730 fail:
731     Py_XDECREF(lon_deg);
732     Py_XDECREF(lat_deg);
733     Py_XDECREF(alt_m);
734     return 0;
735 }
736 
737 
set_station_type(PyObject * self,PyObject * args)738 static PyObject* set_station_type(PyObject* self, PyObject* args)
739 {
740     oskar_Telescope* h = 0;
741     PyObject* capsule = 0;
742     int status = 0;
743     const char* type_string = 0;
744     if (!PyArg_ParseTuple(args, "Os", &capsule, &type_string)) return 0;
745     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
746 
747     /* Check stations exist. */
748     if (oskar_telescope_num_stations(h) == 0)
749     {
750         PyErr_Format(PyExc_RuntimeError, "No stations in telescope model!");
751         return 0;
752     }
753     oskar_telescope_set_station_type(h, type_string, &status);
754     if (status)
755     {
756         PyErr_SetString(PyExc_RuntimeError, "Unknown station type.");
757         return 0;
758     }
759     return Py_BuildValue("");
760 }
761 
762 
set_time_average(PyObject * self,PyObject * args)763 static PyObject* set_time_average(PyObject* self, PyObject* args)
764 {
765     oskar_Telescope* h = 0;
766     PyObject* capsule = 0;
767     double time_average_sec = 0.0;
768     if (!PyArg_ParseTuple(args, "Od", &capsule, &time_average_sec)) return 0;
769     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
770     oskar_telescope_set_time_average(h, time_average_sec);
771     return Py_BuildValue("");
772 }
773 
774 
set_uv_filter(PyObject * self,PyObject * args)775 static PyObject* set_uv_filter(PyObject* self, PyObject* args)
776 {
777     oskar_Telescope* h = 0;
778     PyObject* capsule = 0;
779     int status = 0;
780     double uv_filter_min = 0.0, uv_filter_max = 0.0;
781     const char* units = 0;
782     if (!PyArg_ParseTuple(args, "Odds", &capsule,
783             &uv_filter_min, &uv_filter_max, &units)) return 0;
784     if (!(h = (oskar_Telescope*) get_handle(capsule, name))) return 0;
785 
786     oskar_telescope_set_uv_filter(h,
787             uv_filter_min, uv_filter_max, units, &status);
788     if (status)
789     {
790         PyErr_SetString(PyExc_RuntimeError, "Unknown units.");
791         return 0;
792     }
793     return Py_BuildValue("");
794 }
795 
796 
797 /* Method table. */
798 static PyMethodDef methods[] =
799 {
800         {"capsule_name", (PyCFunction)capsule_name,
801                 METH_VARARGS, "capsule_name()"},
802         {"create", (PyCFunction)create, METH_VARARGS, "create(type)"},
803         {"load", (PyCFunction)load, METH_VARARGS, "load(input_dir)"},
804         {"identical_stations", (PyCFunction)identical_stations,
805                 METH_VARARGS, "identical_stations()"},
806         {"max_station_depth", (PyCFunction)max_station_depth,
807                 METH_VARARGS, "max_station_depth()"},
808         {"max_station_size", (PyCFunction)max_station_size,
809                 METH_VARARGS, "max_station_size()"},
810         {"num_baselines", (PyCFunction)num_baselines,
811                 METH_VARARGS, "num_baselines()"},
812         {"num_stations", (PyCFunction)num_stations,
813                 METH_VARARGS, "num_stations()"},
814         {"override_element_cable_length_errors",
815                 (PyCFunction)override_element_cable_length_errors,
816                 METH_VARARGS,
817                 "override_element_cable_length_errors(feed, seed, mean, std)"},
818         {"override_element_gains", (PyCFunction)override_element_gains,
819                 METH_VARARGS, "override_element_gains(feed, seed, mean, std)"},
820         {"override_element_phases", (PyCFunction)override_element_phases,
821                 METH_VARARGS, "override_element_phases(feed, seed, std)"},
822         {"set_allow_station_beam_duplication",
823                 (PyCFunction)set_allow_station_beam_duplication,
824                 METH_VARARGS, "set_allow_station_beam_duplication(value)"},
825         {"set_channel_bandwidth", (PyCFunction)set_channel_bandwidth,
826                 METH_VARARGS, "set_channel_bandwidth(channel_bandwidth_hz)"},
827         {"set_enable_noise", (PyCFunction)set_enable_noise,
828                 METH_VARARGS, "set_enable_noise(value, seed)"},
829         {"set_enable_numerical_patterns",
830                 (PyCFunction)set_enable_numerical_patterns,
831                 METH_VARARGS, "set_enable_numerical_patterns(value)"},
832         {"set_gaussian_station_beam_width",
833                 (PyCFunction)set_gaussian_station_beam_width, METH_VARARGS,
834                 "set_gaussian_station_beam_width(fwhm_deg, ref_freq_hz)"},
835         {"set_noise_freq", (PyCFunction)set_noise_freq, METH_VARARGS,
836                 "set_noise_freq(start_freq_hz, inc_hz, num_channels)"},
837         {"set_noise_rms", (PyCFunction)set_noise_rms,
838                 METH_VARARGS, "set_noise_rms(start, end)"},
839         {"set_phase_centre", (PyCFunction)set_phase_centre,
840                 METH_VARARGS, "set_phase_centre(ra_rad, dec_rad)"},
841         {"set_pol_mode", (PyCFunction)set_pol_mode,
842                 METH_VARARGS, "set_pol_mode(type)"},
843         {"set_position", (PyCFunction)set_position,
844                 METH_VARARGS, "set_position(longitude, latitude, altitude)"},
845         {"set_station_coords_ecef", (PyCFunction)set_station_coords_ecef,
846                 METH_VARARGS, "set_station_coords_ecef(longitude, latitude, "
847                         "altitude, x, y, z, x_err, y_err, z_err)"},
848         {"set_station_coords_enu", (PyCFunction)set_station_coords_enu,
849                 METH_VARARGS, "set_station_coords_enu(longitude, latitude, "
850                         "altitude, x, y, z, x_err, y_err, z_err)"},
851         {"set_station_coords_wgs84", (PyCFunction)set_station_coords_wgs84,
852                 METH_VARARGS, "set_station_coords_wgs84(longitude, latitude, "
853                         "altitude, station_longitudes, station_latitudes, "
854                         "station_altitudes)"},
855         {"set_station_type", (PyCFunction)set_station_type,
856                 METH_VARARGS, "set_station_type(type_string)"},
857         {"set_time_average", (PyCFunction)set_time_average,
858                 METH_VARARGS, "set_time_average(time_average_sec)"},
859         {"set_uv_filter", (PyCFunction)set_uv_filter, METH_VARARGS,
860                 "set_uv_filter(uv_filter_min, uv_filter_max, units)"},
861         {NULL, NULL, 0, NULL}
862 };
863 
864 
865 #if PY_MAJOR_VERSION >= 3
866 static PyModuleDef moduledef = {
867         PyModuleDef_HEAD_INIT,
868         "_telescope_lib",   /* m_name */
869         module_doc,         /* m_doc */
870         -1,                 /* m_size */
871         methods             /* m_methods */
872 };
873 #endif
874 
875 
moduleinit(void)876 static PyObject* moduleinit(void)
877 {
878     PyObject* m;
879 #if PY_MAJOR_VERSION >= 3
880     m = PyModule_Create(&moduledef);
881 #else
882     m = Py_InitModule3("_telescope_lib", methods, module_doc);
883 #endif
884     return m;
885 }
886 
887 #if PY_MAJOR_VERSION >= 3
PyInit__telescope_lib(void)888 PyMODINIT_FUNC PyInit__telescope_lib(void)
889 {
890     import_array();
891     return moduleinit();
892 }
893 #else
894 /* The init function name has to match that of the compiled module
895  * with the pattern 'init<module name>'. This module is called '_telescope_lib' */
init_telescope_lib(void)896 PyMODINIT_FUNC init_telescope_lib(void)
897 {
898     import_array();
899     moduleinit();
900     return;
901 }
902 #endif
903 
904