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