1 /*
2  * Copyright (c) 2015-2021, The OSKAR Developers.
3  * See the LICENSE file at the top-level directory of this distribution.
4  */
5 
6 #include "math/oskar_find_closest_match.h"
7 #include "math/oskar_random_gaussian.h"
8 #include "vis/oskar_vis_block.h"
9 #include "vis/private_vis_block.h"
10 #include <math.h>
11 
12 #ifdef __cplusplus
13 extern "C" {
14 #endif
15 
oskar_get_station_std_dev_for_channel(oskar_Mem * station_std_dev,double frequency_hz,const oskar_Telescope * tel,int * status)16 static void oskar_get_station_std_dev_for_channel(oskar_Mem* station_std_dev,
17         double frequency_hz, const oskar_Telescope* tel, int* status)
18 {
19     int i = 0, j = 0;
20     const oskar_Mem *noise_freq = 0, *noise_rms = 0;
21 
22     /* Ensure output array is big enough. */
23     const int num_stations = oskar_telescope_num_stations(tel);
24     oskar_mem_ensure(station_std_dev, num_stations, status);
25 
26     /* Loop over stations and get noise value standard deviation for each. */
27     const int* type_map = oskar_mem_int_const(
28             oskar_telescope_station_type_map_const(tel), status);
29     for (i = 0; i < num_stations; ++i)
30     {
31         const oskar_Station* station =
32                 oskar_telescope_station_const(tel, type_map[i]);
33         if (!station) station = oskar_telescope_station_const(tel, 0);
34         noise_freq = oskar_station_noise_freq_hz_const(station);
35         noise_rms = oskar_station_noise_rms_jy_const(station);
36         j = oskar_find_closest_match(frequency_hz, noise_freq, status);
37         oskar_mem_copy_contents(station_std_dev, noise_rms, i, j, 1, status);
38     }
39 }
40 
41 /* Applies noise to data in a visibility block, for the given channel. */
oskar_vis_block_apply_noise(oskar_VisBlock * vis,const oskar_Mem * station_std_dev,unsigned int seed,int global_slice_idx,int local_slice_idx,double channel_bandwidth_hz,double time_int_sec,int * status)42 static void oskar_vis_block_apply_noise(oskar_VisBlock* vis,
43         const oskar_Mem* station_std_dev, unsigned int seed,
44         int global_slice_idx, int local_slice_idx,
45         double channel_bandwidth_hz, double time_int_sec, int* status)
46 {
47     int a1 = 0, a2 = 0, b = 0, c = 0;
48     void *acorr_ptr = 0, *xcorr_ptr = 0;
49     double rnd[8];
50     const double inv_sqrt2 = 1.0 / sqrt(2.0);
51 
52     /* Get pointer to start of block, and block dimensions. */
53     acorr_ptr = oskar_mem_void(oskar_vis_block_auto_correlations(vis));
54     xcorr_ptr = oskar_mem_void(oskar_vis_block_cross_correlations(vis));
55     const int have_autocorr  = oskar_vis_block_has_auto_correlations(vis);
56     const int have_crosscorr = oskar_vis_block_has_cross_correlations(vis);
57     const int num_baselines  = oskar_vis_block_num_baselines(vis);
58     const int num_stations   = oskar_vis_block_num_stations(vis);
59 
60     /* Get factor for conversion of sigma to SEFD. */
61     const double sefd_factor = sqrt(2.0 * channel_bandwidth_hz * time_int_sec);
62 
63     /* If we are adding noise directly to Stokes I, the noise is defined
64      * as single dipole noise, so we have to divide by sqrt(2) to take into
65      * account of the two different dipoles that go into the calculation of
66      * Stokes I. For polarised visibilities this is not required, as this
67      * falls out naturally when evaluating Stokes I from the dipole
68      * correlations (i.e. I = 0.5 (XX+YY) ). */
69 
70     switch (oskar_mem_type(oskar_vis_block_cross_correlations(vis)))
71     {
72     case OSKAR_SINGLE_COMPLEX:
73     {
74         const float* st_std = oskar_mem_float_const(station_std_dev, status);
75         if (have_crosscorr)
76         {
77             float2* data = (float2*) xcorr_ptr + (num_baselines * local_slice_idx);
78             for (a1 = 0, b = 0; a1 < num_stations; ++a1)
79             {
80                 for (a2 = a1 + 1; a2 < num_stations; ++b, ++a2)
81                 {
82                     oskar_random_gaussian2(seed, c++, global_slice_idx, rnd);
83                     const double std = sqrt(st_std[a1] * st_std[a2]) * inv_sqrt2;
84                     data[b].x += std * rnd[0];
85                     data[b].y += std * rnd[1];
86                 }
87             }
88         }
89 
90         if (have_autocorr)
91         {
92             /* Autocorrelation noise. Phases are all zero after
93              * autocorrelation, so ignore the imaginary components. */
94             float2* data = (float2*) acorr_ptr + (num_stations * local_slice_idx);
95             for (a1 = 0; a1 < num_stations; ++a1)
96             {
97                 oskar_random_gaussian2(seed, c++, global_slice_idx, rnd);
98                 const double std = st_std[a1];
99                 const double mean = sqrt(2.0) * st_std[a1];
100                 data[a1].x += std * rnd[0] + mean * sefd_factor;
101             }
102         }
103         break;
104     }
105     case OSKAR_SINGLE_COMPLEX_MATRIX:
106     {
107         const float* st_std = oskar_mem_float_const(station_std_dev, status);
108         if (have_crosscorr)
109         {
110             float4c* data = (float4c*) xcorr_ptr + (num_baselines * local_slice_idx);
111             for (a1 = 0, b = 0; a1 < num_stations; ++a1)
112             {
113                 for (a2 = a1 + 1; a2 < num_stations; ++b, ++a2)
114                 {
115                     oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd);
116                     oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd + 4);
117                     const double std = sqrt(st_std[a1] * st_std[a2]);
118                     data[b].a.x += std * rnd[0];
119                     data[b].a.y += std * rnd[1];
120                     data[b].b.x += std * rnd[2];
121                     data[b].b.y += std * rnd[3];
122                     data[b].c.x += std * rnd[4];
123                     data[b].c.y += std * rnd[5];
124                     data[b].d.x += std * rnd[6];
125                     data[b].d.y += std * rnd[7];
126                 }
127             }
128         }
129 
130         if (have_autocorr)
131         {
132             /* Autocorrelation noise. Phases are all zero after
133              * autocorrelation, so ignore the imaginary components. */
134             float4c* data = (float4c*) acorr_ptr + (num_stations * local_slice_idx);
135             for (a1 = 0; a1 < num_stations; ++a1)
136             {
137                 oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd);
138                 oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd + 4);
139                 const double std = st_std[a1] * sqrt(2.0);
140                 const double mean = std * sefd_factor;
141                 data[a1].a.x += std * rnd[0] + mean;
142                 data[a1].b.x += std * rnd[1];
143                 data[a1].b.y += std * rnd[2];
144                 data[a1].c.x += std * rnd[3];
145                 data[a1].c.y += std * rnd[4];
146                 data[a1].d.x += std * rnd[5] + mean;
147             }
148         }
149         break;
150     }
151     case OSKAR_DOUBLE_COMPLEX:
152     {
153         const double* st_std = oskar_mem_double_const(station_std_dev, status);
154         if (have_crosscorr)
155         {
156             double2* data = (double2*) xcorr_ptr + (num_baselines * local_slice_idx);
157             for (a1 = 0, b = 0; a1 < num_stations; ++a1)
158             {
159                 for (a2 = a1 + 1; a2 < num_stations; ++b, ++a2)
160                 {
161                     oskar_random_gaussian2(seed, c++, global_slice_idx, rnd);
162                     const double std = sqrt(st_std[a1] * st_std[a2]) * inv_sqrt2;
163                     data[b].x += std * rnd[0];
164                     data[b].y += std * rnd[1];
165                 }
166             }
167         }
168 
169         if (have_autocorr)
170         {
171             /* Autocorrelation noise. Phases are all zero after
172              * autocorrelation, so ignore the imaginary components. */
173             double2* data = (double2*) acorr_ptr + (num_stations * local_slice_idx);
174             for (a1 = 0; a1 < num_stations; ++a1)
175             {
176                 oskar_random_gaussian2(seed, c++, global_slice_idx, rnd);
177                 const double std  = st_std[a1];
178                 const double mean = st_std[a1] * sefd_factor * sqrt(2.0);
179                 data[a1].x += std * rnd[0] + mean;
180             }
181         }
182         break;
183     }
184     case OSKAR_DOUBLE_COMPLEX_MATRIX:
185     {
186         const double* st_std = oskar_mem_double_const(station_std_dev, status);
187         if (have_crosscorr)
188         {
189             double4c* data = (double4c*) xcorr_ptr + (num_baselines * local_slice_idx);
190             for (a1 = 0, b = 0; a1 < num_stations; ++a1)
191             {
192                 for (a2 = a1 + 1; a2 < num_stations; ++b, ++a2)
193                 {
194                     oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd);
195                     oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd + 4);
196                     const double std = sqrt(st_std[a1] * st_std[a2]);
197                     data[b].a.x += std * rnd[0];
198                     data[b].a.y += std * rnd[1];
199                     data[b].b.x += std * rnd[2];
200                     data[b].b.y += std * rnd[3];
201                     data[b].c.x += std * rnd[4];
202                     data[b].c.y += std * rnd[5];
203                     data[b].d.x += std * rnd[6];
204                     data[b].d.y += std * rnd[7];
205                 }
206             }
207         }
208 
209         if (have_autocorr)
210         {
211             /* Autocorrelation noise. Phases are all zero after
212              * autocorrelation, so ignore the imaginary components. */
213             double4c* data = (double4c*) acorr_ptr + (num_stations * local_slice_idx);
214             for (a1 = 0; a1 < num_stations; ++a1)
215             {
216                 oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd);
217                 oskar_random_gaussian4(seed, c++, global_slice_idx, 0, 0, rnd + 4);
218                 const double std  = st_std[a1] * sqrt(2.0);
219                 const double mean = std * sefd_factor;
220                 data[a1].a.x += std * rnd[0] + mean;
221                 data[a1].b.x += std * rnd[1];
222                 data[a1].b.y += std * rnd[2];
223                 data[a1].c.x += std * rnd[3];
224                 data[a1].c.y += std * rnd[4];
225                 data[a1].d.x += std * rnd[5] + mean;
226             }
227         }
228         break;
229     }
230     };
231 }
232 
oskar_vis_block_add_system_noise(oskar_VisBlock * vis,const oskar_VisHeader * header,const oskar_Telescope * telescope,oskar_Mem * station_work,int * status)233 void oskar_vis_block_add_system_noise(oskar_VisBlock* vis,
234         const oskar_VisHeader* header, const oskar_Telescope* telescope,
235         oskar_Mem* station_work, int* status)
236 {
237     int t = 0, c = 0;
238     int num_times_block = 0, num_channels_block = 0, num_channels_total = 0;
239     int start_time = 0, start_channel = 0;
240     unsigned int seed = 0;
241     double freq_start_hz = 0.0, freq_inc_hz = 0.0;
242     double channel_bandwidth_hz = 0.0, time_int_sec = 0.0;
243     if (*status) return;
244 
245     /* Check baseline dimensions match. */
246     if (oskar_telescope_num_baselines(telescope) !=
247             oskar_vis_block_num_baselines(vis))
248     {
249         *status = OSKAR_ERR_DIMENSION_MISMATCH;
250         return;
251     }
252 
253     /* Get frequency start and increment. */
254     seed                 = oskar_telescope_noise_seed(telescope);
255     num_times_block      = oskar_vis_block_num_times(vis);
256     num_channels_block   = oskar_vis_block_num_channels(vis);
257     num_channels_total   = oskar_vis_header_num_channels_total(header);
258     start_channel        = oskar_vis_block_start_channel_index(vis);
259     start_time           = oskar_vis_block_start_time_index(vis);
260     channel_bandwidth_hz = oskar_vis_header_channel_bandwidth_hz(header);
261     time_int_sec         = oskar_vis_header_time_average_sec(header);
262     freq_start_hz        = oskar_vis_header_freq_start_hz(header);
263     freq_inc_hz          = oskar_vis_header_freq_inc_hz(header);
264 
265     /* Loop over channels in the block. */
266     for (c = 0; c < num_channels_block; ++c)
267     {
268         const int channel_index = c + start_channel;
269         const double freq_hz = freq_start_hz + channel_index * freq_inc_hz;
270         oskar_get_station_std_dev_for_channel(station_work, freq_hz,
271                 telescope, status);
272 
273         /* Loop over time samples in the block. */
274         for (t = 0; t < num_times_block; ++t)
275         {
276             /* Get slice indices. */
277             const int local_slice_index = t * num_channels_block + c;
278             const int global_slice_index =
279                     (t + start_time) * num_channels_total + channel_index;
280 
281             /* Add noise to the slice. */
282             oskar_vis_block_apply_noise(vis, station_work, seed,
283                     global_slice_index, local_slice_index,
284                     channel_bandwidth_hz, time_int_sec, status);
285         }
286     }
287 }
288 
289 #ifdef __cplusplus
290 }
291 #endif
292