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