1 /*
2 * Copyright (c) 2013-2015, 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 "interferometer/oskar_evaluate_jones_Z.h"
30
31 #include "convert/oskar_convert_relative_directions_to_enu_directions.h"
32 #include "convert/oskar_convert_offset_ecef_to_ecef.h"
33 #include "telescope/station/oskar_evaluate_pierce_points.h"
34 #include "sky/oskar_evaluate_tec_tid.h"
35
36 #include <math.h>
37 #include <stdio.h>
38
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42
43 static void oskar_evaluate_TEC(oskar_WorkJonesZ* work, int num_pp,
44 const oskar_SettingsIonosphere* settings,
45 double gast, int* status);
46
47 static void evaluate_station_ECEF_coords(
48 double* station_x, double* station_y, double* station_z,
49 int stationID, const oskar_Telescope* telescope);
50
51 static void evaluate_jones_Z_station(double wavelength,
52 const oskar_Mem* TEC, const oskar_Mem* hor_z,
53 double min_elevation, int num_pp,
54 int offset_out, oskar_Mem* out, int* status);
55
oskar_evaluate_jones_Z(oskar_Jones * Z,const oskar_Sky * sky,const oskar_Telescope * telescope,const oskar_SettingsIonosphere * settings,double gast,double frequency_hz,oskar_WorkJonesZ * work,int * status)56 void oskar_evaluate_jones_Z(oskar_Jones* Z, const oskar_Sky* sky,
57 const oskar_Telescope* telescope,
58 const oskar_SettingsIonosphere* settings, double gast,
59 double frequency_hz, oskar_WorkJonesZ* work, int* status)
60 {
61 int i, num_sources, num_stations;
62 /* Station position in ECEF frame */
63 double station_x, station_y, station_z, wavelength;
64 oskar_Sky* sky_cpu; /* Copy of the sky model on the CPU */
65
66 /* Check if safe to proceed. */
67 if (*status) return;
68
69 /* Check data types. */
70 const int type = oskar_sky_precision(sky);
71 if (oskar_telescope_precision(telescope) != type ||
72 oskar_jones_type(Z) != (type | OSKAR_COMPLEX))
73 {
74 *status = OSKAR_ERR_BAD_DATA_TYPE;
75 return;
76 }
77
78 /* For now, this function requires data is on the CPU .. check this. */
79
80 /* Resize the work array (if needed) */
81 num_stations = oskar_telescope_num_stations(telescope);
82 num_sources = oskar_sky_num_sources(sky);
83 oskar_work_jones_z_resize(work, num_sources, status);
84
85 /* Copy the sky model to the CPU. */
86 sky_cpu = oskar_sky_create_copy(sky, OSKAR_CPU, status);
87
88 wavelength = 299792458.0 / frequency_hz;
89
90 /* Evaluate the ionospheric phase screen for each station at each
91 * source pierce point. */
92 for (i = 0; i < num_stations; ++i)
93 {
94 double last, lon, lat;
95 const oskar_Station* station;
96 station = oskar_telescope_station_const(telescope, i);
97 lon = oskar_station_lon_rad(station);
98 lat = oskar_station_lat_rad(station);
99 last = gast + lon;
100
101 /* Evaluate horizontal x,y,z source positions (for which to evaluate
102 * pierce points) */
103 oskar_convert_relative_directions_to_enu_directions(0, 0, 0, num_sources,
104 oskar_sky_l_const(sky_cpu), oskar_sky_m_const(sky_cpu),
105 oskar_sky_n_const(sky_cpu), last - oskar_sky_reference_ra_rad(sky_cpu),
106 oskar_sky_reference_dec_rad(sky_cpu), lat,
107 0, work->hor_x, work->hor_y, work->hor_z, status);
108
109 /* Obtain station coordinates in the ECEF frame. */
110 evaluate_station_ECEF_coords(&station_x, &station_y, &station_z, i,
111 telescope);
112
113 /* Obtain the pierce points. */
114 /* FIXME(BM) this is current hard-coded to TID height screen 0
115 * this fix is only needed to support multiple screen heights. */
116 oskar_evaluate_pierce_points(work->pp_lon, work->pp_lat,
117 work->pp_rel_path, station_x, station_y,
118 station_z, settings->TID[0].height_km * 1000., num_sources,
119 work->hor_x, work->hor_y, work->hor_z, status);
120
121 /* Evaluate TEC values for the pierce points */
122 oskar_evaluate_TEC(work, num_sources, settings, gast, status);
123
124 /* Populate the Jones matrix with ionospheric phase */
125 const int offset_out = i * oskar_jones_num_sources(Z);
126 evaluate_jones_Z_station(wavelength,
127 work->total_TEC, work->hor_z, settings->min_elevation,
128 num_sources, offset_out, oskar_jones_mem(Z), status);
129 }
130
131 oskar_sky_free(sky_cpu, status);
132 }
133
134
135 /* Evaluate the TEC value for each pierce point - note: at the moment this is
136 * just the accumulation of one or more TID screens.
137 * TODO convert this to a stand-alone function.
138 */
oskar_evaluate_TEC(oskar_WorkJonesZ * work,int num_pp,const oskar_SettingsIonosphere * settings,double gast,int * status)139 static void oskar_evaluate_TEC(oskar_WorkJonesZ* work, int num_pp,
140 const oskar_SettingsIonosphere* settings,
141 double gast, int* status)
142 {
143 int i;
144
145 /* FIXME(BM) For now limit number of screens to 1, this can be removed
146 * if a TEC model which is valid for multiple screens is implemented
147 */
148 if (settings->num_TID_screens > 1)
149 *status = OSKAR_ERR_INVALID_ARGUMENT;
150
151 oskar_mem_clear_contents(work->total_TEC, status);
152
153 /* Loop over TID screens to evaluate TEC values */
154 for (i = 0; i < settings->num_TID_screens; ++i)
155 {
156 oskar_mem_clear_contents(work->screen_TEC, status);
157
158 /* Evaluate TEC values for the screen */
159 oskar_evaluate_tec_tid(work->screen_TEC, num_pp, work->pp_lon,
160 work->pp_lat, work->pp_rel_path, settings->TEC0,
161 &settings->TID[i], gast);
162
163 /* Accumulate into total TEC */
164 /* FIXME(BM) addition is not physical for more than one TEC screen in
165 * the way TIDs are currently evaluated as TEC0 is added into both
166 * screens.
167 */
168 oskar_mem_add(work->total_TEC, work->total_TEC, work->screen_TEC,
169 0, 0, 0, oskar_mem_length(work->total_TEC), status);
170 }
171 }
172
173
evaluate_station_ECEF_coords(double * station_x,double * station_y,double * station_z,int stationID,const oskar_Telescope * telescope)174 static void evaluate_station_ECEF_coords(
175 double* station_x, double* station_y, double* station_z,
176 int stationID, const oskar_Telescope* telescope)
177 {
178 double st_x, st_y, st_z;
179 double lon, lat, alt;
180 const oskar_Station* station;
181 const void *x_, *y_, *z_;
182
183 x_ = oskar_mem_void_const(
184 oskar_telescope_station_true_offset_ecef_metres_const(telescope, 0));
185 y_ = oskar_mem_void_const(
186 oskar_telescope_station_true_offset_ecef_metres_const(telescope, 1));
187 z_ = oskar_mem_void_const(
188 oskar_telescope_station_true_offset_ecef_metres_const(telescope, 2));
189 station = oskar_telescope_station_const(telescope, stationID);
190 lon = oskar_station_lon_rad(station);
191 lat = oskar_station_lat_rad(station);
192 alt = oskar_station_alt_metres(station);
193
194 if (oskar_mem_type(
195 oskar_telescope_station_true_offset_ecef_metres_const(telescope, 0)) ==
196 OSKAR_DOUBLE)
197 {
198 st_x = ((const double*)x_)[stationID];
199 st_y = ((const double*)y_)[stationID];
200 st_z = ((const double*)z_)[stationID];
201 }
202 else
203 {
204 st_x = (double)((const float*)x_)[stationID];
205 st_y = (double)((const float*)y_)[stationID];
206 st_z = (double)((const float*)z_)[stationID];
207 }
208
209 oskar_convert_offset_ecef_to_ecef(1, &st_x, &st_y, &st_z, lon, lat, alt,
210 station_x, station_y, station_z);
211 }
212
evaluate_jones_Z_station(double wavelength,const oskar_Mem * TEC,const oskar_Mem * hor_z,double min_elevation,int num_pp,int offset_out,oskar_Mem * out,int * status)213 static void evaluate_jones_Z_station(double wavelength,
214 const oskar_Mem* TEC, const oskar_Mem* hor_z,
215 double min_elevation, int num_pp,
216 int offset_out, oskar_Mem* out, int* status)
217 {
218 int i, type;
219 double arg;
220
221 /* Check if safe to proceed. */
222 if (*status) return;
223
224 type = oskar_mem_type(out);
225 if (type == OSKAR_DOUBLE_COMPLEX)
226 {
227 double2* Z_ = oskar_mem_double2(out, status) + offset_out;
228 for (i = 0; i < num_pp; ++i)
229 {
230 /* Initialise as an unit scalar Z = (1 + 0i) i.e. no phase change */
231 Z_[i].x = 1.0;
232 Z_[i].y = 0.0;
233
234 /* If the pierce point is below the minimum specified elevation
235 * don't evaluate a phase */
236 if (asin((oskar_mem_double_const(hor_z, status))[i]) <
237 min_elevation)
238 continue;
239
240 arg = wavelength * 25. * oskar_mem_double_const(TEC, status)[i];
241
242 /* Z phase == exp(i * lambda * 25 * tec) */
243 Z_[i].x = cos(arg);
244 Z_[i].y = sin(arg);
245 }
246 }
247 else if (type == OSKAR_SINGLE_COMPLEX)
248 {
249 float2* Z_ = oskar_mem_float2(out, status) + offset_out;
250 for (i = 0; i < num_pp; ++i)
251 {
252 /* Initialise as an unit scalar Z = (1 + 0i) i.e. no phase change */
253 Z_[i].x = 1.0;
254 Z_[i].y = 0.0;
255
256 /* If the pierce point is below the minimum specified elevation
257 * don't evaluate a phase */
258 if (asin((oskar_mem_float_const(hor_z, status))[i]) <
259 min_elevation)
260 continue;
261
262 arg = wavelength * 25. * oskar_mem_float_const(TEC, status)[i];
263
264 /* Z phase == exp(i * lambda * 25 * tec) */
265 Z_[i].x = cos(arg);
266 Z_[i].y = sin(arg);
267 }
268 }
269 else
270 {
271 *status = OSKAR_ERR_BAD_DATA_TYPE;
272 }
273 }
274
275 #ifdef __cplusplus
276 }
277 #endif
278