void oskar_station_override_element_time_variable_phases(oskar_Station* s, double phase_std, int* status) { int i; /* Check if safe to proceed. */ if (*status) return; /* Check if there are child stations. */ if (oskar_station_has_child(s)) { /* Recursive call to find the last level (i.e. the element data). */ for (i = 0; i < s->num_elements; ++i) { oskar_station_override_element_time_variable_phases( oskar_station_child(s, i), phase_std, status); } } else { /* Override element data at last level. */ oskar_mem_set_value_real(s->element_phase_error_rad, phase_std, 0, 0, status); } }
void oskar_evaluate_station_beam_aperture_array(oskar_Mem* beam, const oskar_Station* station, int num_points, const oskar_Mem* x, const oskar_Mem* y, const oskar_Mem* z, double gast, double frequency_hz, oskar_StationWork* work, int time_index, int* status) { int start; /* Check if safe to proceed. */ if (*status) return; /* Evaluate beam immediately, without chunking, if there are no * child stations. */ if (!oskar_station_has_child(station)) { oskar_evaluate_station_beam_aperture_array_private(beam, station, num_points, x, y, z, gast, frequency_hz, work, time_index, 0, status); } else { oskar_Mem *c_beam, *c_x, *c_y, *c_z; c_beam = oskar_mem_create_alias(0, 0, 0, status); c_x = oskar_mem_create_alias(0, 0, 0, status); c_y = oskar_mem_create_alias(0, 0, 0, status); c_z = oskar_mem_create_alias(0, 0, 0, status); /* Split up list of input points into manageable chunks. */ for (start = 0; start < num_points; start += MAX_CHUNK_SIZE) { int chunk_size; /* Get size of current chunk. */ chunk_size = num_points - start; if (chunk_size > MAX_CHUNK_SIZE) chunk_size = MAX_CHUNK_SIZE; /* Get pointers to start of chunk input data. */ oskar_mem_set_alias(c_beam, beam, start, chunk_size, status); oskar_mem_set_alias(c_x, x, start, chunk_size, status); oskar_mem_set_alias(c_y, y, start, chunk_size, status); oskar_mem_set_alias(c_z, z, start, chunk_size, status); /* Start recursive call at depth 1 (depth 0 is element level). */ oskar_evaluate_station_beam_aperture_array_private(c_beam, station, chunk_size, c_x, c_y, c_z, gast, frequency_hz, work, time_index, 1, status); } /* Release handles for chunk memory. */ oskar_mem_free(c_beam, status); oskar_mem_free(c_x, status); oskar_mem_free(c_y, status); oskar_mem_free(c_z, status); } }
static void oskar_telescope_set_station_type_p(oskar_Station* station, int type) { oskar_station_set_station_type(station, type); if (oskar_station_has_child(station)) { int i, num_elements; num_elements = oskar_station_num_elements(station); for (i = 0; i < num_elements; ++i) { oskar_telescope_set_station_type_p( oskar_station_child(station, i), type); } } }
void oskar_station_override_element_feed_angle(oskar_Station* s, unsigned int seed, int x_pol, double alpha_error_rad, double beta_error_rad, double gamma_error_rad, int* status) { int i; /* Check if safe to proceed. */ if (*status) return; /* Check if there are child stations. */ if (oskar_station_has_child(s)) { /* Recursive call to find the last level (i.e. the element data). */ for (i = 0; i < s->num_elements; ++i) { oskar_station_override_element_feed_angle( oskar_station_child(s, i), seed, x_pol, alpha_error_rad, beta_error_rad, gamma_error_rad, status); } } else { /* Override element data at last level. */ int id; double *a, *b, *c, r[4]; oskar_Mem *alpha, *beta, *gamma; /* Get pointer to the X or Y element orientation data. */ alpha = x_pol ? s->element_x_alpha_cpu : s->element_y_alpha_cpu; beta = x_pol ? s->element_x_beta_cpu : s->element_y_beta_cpu; gamma = x_pol ? s->element_x_gamma_cpu : s->element_y_gamma_cpu; a = oskar_mem_double(alpha, status); b = oskar_mem_double(beta, status); c = oskar_mem_double(gamma, status); id = oskar_station_unique_id(s); for (i = 0; i < s->num_elements; ++i) { /* Generate random numbers from Gaussian distribution. */ oskar_random_gaussian4(seed, i, id, 0, 0, r); r[0] *= alpha_error_rad; r[1] *= beta_error_rad; r[2] *= gamma_error_rad; /* Set the new angle. */ a[i] += r[0]; b[i] += r[1]; c[i] += r[2]; } } }
static void oskar_telescope_set_gaussian_station_beam_p(oskar_Station* station, double fwhm_rad, double ref_freq_hz) { oskar_station_set_gaussian_beam_values(station, fwhm_rad, ref_freq_hz); if (oskar_station_has_child(station)) { int i, num_elements; num_elements = oskar_station_num_elements(station); for (i = 0; i < num_elements; ++i) { oskar_telescope_set_gaussian_station_beam_p( oskar_station_child(station, i), fwhm_rad, ref_freq_hz); } } }
static void max_station_size_and_depth(const oskar_Station* s, int* max_elements, int* max_depth, int depth) { int i, num_elements; num_elements = oskar_station_num_elements(s); *max_elements = num_elements > *max_elements ? num_elements : *max_elements; *max_depth = depth > *max_depth ? depth : *max_depth; if (oskar_station_has_child(s)) { for (i = 0; i < num_elements; ++i) { max_station_size_and_depth(oskar_station_child_const(s, i), max_elements, max_depth, depth + 1); } } }
static void oskar_telescope_set_station_phase_centre(oskar_Station* station, int coord_type, double ra_rad, double dec_rad) { oskar_station_set_phase_centre(station, coord_type, ra_rad, dec_rad); if (oskar_station_has_child(station)) { int i, num_elements; num_elements = oskar_station_num_elements(station); for (i = 0; i < num_elements; ++i) { oskar_telescope_set_station_phase_centre( oskar_station_child(station, i), coord_type, ra_rad, dec_rad); } } }
void oskar_station_set_polar_motion(oskar_Station* model, double pm_x_rad, double pm_y_rad) { int i; model->pm_x_rad = pm_x_rad; model->pm_y_rad = pm_y_rad; /* Set recursively for all child stations. */ if (oskar_station_has_child(model)) { for (i = 0; i < model->num_elements; ++i) { oskar_station_set_polar_motion(model->child[i], pm_x_rad, pm_y_rad); } } }
static void set_child_station_locations(oskar_Station* s, const oskar_Station* parent) { if (parent) { oskar_station_set_position(s, oskar_station_lon_rad(parent), oskar_station_lat_rad(parent), oskar_station_alt_metres(parent)); } /* Recursively set data for child stations. */ if (oskar_station_has_child(s)) { int i, num_elements; num_elements = oskar_station_num_elements(s); for (i = 0; i < num_elements; ++i) set_child_station_locations(oskar_station_child(s, i), s); } }
oskar_Station* oskar_station_create_copy(const oskar_Station* src, int location, int* status) { int i = 0; oskar_Station* model = 0; /* Check if safe to proceed. */ if (*status) return model; /* Create the new model. */ model = oskar_station_create(oskar_station_precision(src), location, oskar_station_num_elements(src), status); /* Set meta-data. */ model->unique_id = src->unique_id; model->precision = src->precision; model->mem_location = location; /* Copy common station parameters. */ model->station_type = src->station_type; model->normalise_final_beam = src->normalise_final_beam; model->lon_rad = src->lon_rad; model->lat_rad = src->lat_rad; model->alt_metres = src->alt_metres; model->pm_x_rad = src->pm_x_rad; model->pm_y_rad = src->pm_y_rad; model->beam_lon_rad = src->beam_lon_rad; model->beam_lat_rad = src->beam_lat_rad; model->beam_coord_type = src->beam_coord_type; oskar_mem_copy(model->noise_freq_hz, src->noise_freq_hz, status); oskar_mem_copy(model->noise_rms_jy, src->noise_rms_jy, status); /* Copy aperture array data, except num_element_types (done later). */ model->identical_children = src->identical_children; model->num_elements = src->num_elements; model->normalise_array_pattern = src->normalise_array_pattern; model->enable_array_pattern = src->enable_array_pattern; model->common_element_orientation = src->common_element_orientation; model->array_is_3d = src->array_is_3d; model->apply_element_errors = src->apply_element_errors; model->apply_element_weight = src->apply_element_weight; model->seed_time_variable_errors = src->seed_time_variable_errors; model->num_permitted_beams = src->num_permitted_beams; /* Copy Gaussian station beam data. */ model->gaussian_beam_fwhm_rad = src->gaussian_beam_fwhm_rad; model->gaussian_beam_reference_freq_hz = src->gaussian_beam_reference_freq_hz; /* Copy memory blocks. */ oskar_mem_copy(model->element_true_x_enu_metres, src->element_true_x_enu_metres, status); oskar_mem_copy(model->element_true_y_enu_metres, src->element_true_y_enu_metres, status); oskar_mem_copy(model->element_true_z_enu_metres, src->element_true_z_enu_metres, status); oskar_mem_copy(model->element_measured_x_enu_metres, src->element_measured_x_enu_metres, status); oskar_mem_copy(model->element_measured_y_enu_metres, src->element_measured_y_enu_metres, status); oskar_mem_copy(model->element_measured_z_enu_metres, src->element_measured_z_enu_metres, status); oskar_mem_copy(model->element_weight, src->element_weight, status); oskar_mem_copy(model->element_gain, src->element_gain, status); oskar_mem_copy(model->element_gain_error, src->element_gain_error, status); oskar_mem_copy(model->element_phase_offset_rad, src->element_phase_offset_rad, status); oskar_mem_copy(model->element_phase_error_rad, src->element_phase_error_rad, status); oskar_mem_copy(model->element_x_alpha_cpu, src->element_x_alpha_cpu, status); oskar_mem_copy(model->element_x_beta_cpu, src->element_x_beta_cpu, status); oskar_mem_copy(model->element_x_gamma_cpu, src->element_x_gamma_cpu, status); oskar_mem_copy(model->element_y_alpha_cpu, src->element_y_alpha_cpu, status); oskar_mem_copy(model->element_y_beta_cpu, src->element_y_beta_cpu, status); oskar_mem_copy(model->element_y_gamma_cpu, src->element_y_gamma_cpu, status); oskar_mem_copy(model->element_types, src->element_types, status); oskar_mem_copy(model->element_types_cpu, src->element_types_cpu, status); oskar_mem_copy(model->element_mount_types_cpu, src->element_mount_types_cpu, status); oskar_mem_copy(model->permitted_beam_az_rad, src->permitted_beam_az_rad, status); oskar_mem_copy(model->permitted_beam_el_rad, src->permitted_beam_el_rad, status); /* Copy element models, if set. */ if (oskar_station_has_element(src)) { /* Ensure enough space for element model data. */ oskar_station_resize_element_types(model, src->num_element_types, status); /* Copy the element model data. */ for (i = 0; i < src->num_element_types; ++i) { oskar_element_copy(model->element[i], src->element[i], status); } } /* Recursively copy child stations. */ if (oskar_station_has_child(src)) { model->child = malloc(src->num_elements * sizeof(oskar_Station*)); if (!model->child) { *status = OSKAR_ERR_MEMORY_ALLOC_FAILURE; return model; } for (i = 0; i < src->num_elements; ++i) { model->child[i] = oskar_station_create_copy( oskar_station_child_const(src, i), location, status); } } return model; }
static void oskar_evaluate_station_beam_aperture_array_private(oskar_Mem* beam, const oskar_Station* s, int num_points, const oskar_Mem* x, const oskar_Mem* y, const oskar_Mem* z, double gast, double frequency_hz, oskar_StationWork* work, int time_index, int depth, int* status) { double beam_x, beam_y, beam_z, wavenumber; oskar_Mem *weights, *weights_error, *theta, *phi, *array; int num_elements, is_3d; num_elements = oskar_station_num_elements(s); is_3d = oskar_station_array_is_3d(s); weights = work->weights; weights_error = work->weights_error; theta = work->theta_modified; phi = work->phi_modified; array = work->array_pattern; wavenumber = 2.0 * M_PI * frequency_hz / 299792458.0; /* Check if safe to proceed. */ if (*status) return; /* Compute direction cosines for the beam for this station. */ oskar_evaluate_beam_horizon_direction(&beam_x, &beam_y, &beam_z, s, gast, status); /* Evaluate beam if there are no child stations. */ if (!oskar_station_has_child(s)) { /* First optimisation: A single element model type, and either a common * orientation for all elements within the station, or isotropic * elements. */ /* Array pattern and element pattern are separable. */ if (oskar_station_num_element_types(s) == 1 && (oskar_station_common_element_orientation(s) || oskar_element_type(oskar_station_element_const(s, 0)) == OSKAR_ELEMENT_TYPE_ISOTROPIC) ) { /* (Always) evaluate element pattern into the output beam array. */ oskar_element_evaluate(oskar_station_element_const(s, 0), beam, oskar_station_element_x_alpha_rad(s, 0) + M_PI/2.0, /* FIXME Will change: This matches the old convention. */ oskar_station_element_y_alpha_rad(s, 0), num_points, x, y, z, frequency_hz, theta, phi, status); /* Check if array pattern is enabled. */ if (oskar_station_enable_array_pattern(s)) { /* Generate beamforming weights and evaluate array pattern. */ oskar_evaluate_element_weights(weights, weights_error, wavenumber, s, beam_x, beam_y, beam_z, time_index, status); oskar_dftw(num_elements, wavenumber, oskar_station_element_true_x_enu_metres_const(s), oskar_station_element_true_y_enu_metres_const(s), oskar_station_element_true_z_enu_metres_const(s), weights, num_points, x, y, (is_3d ? z : 0), 0, array, status); /* Normalise array response if required. */ if (oskar_station_normalise_array_pattern(s)) oskar_mem_scale_real(array, 1.0 / num_elements, status); /* Element-wise multiply to join array and element pattern. */ oskar_mem_multiply(beam, beam, array, num_points, status); } } #if 0 /* Second optimisation: Common orientation for all elements within the * station, but more than one element type. */ else if (oskar_station_common_element_orientation(s)) { /* Must evaluate array pattern, so check that this is enabled. */ if (!oskar_station_enable_array_pattern(s)) { *status = OSKAR_ERR_SETTINGS_TELESCOPE; return; } /* Call a DFT using indexed input. */ /* TODO Not yet implemented. */ *status = OSKAR_ERR_FUNCTION_NOT_AVAILABLE; } #endif /* No optimisation: No common element orientation. */ /* Can't separate array and element evaluation. */ else { int i, num_element_types; oskar_Mem *element_block = 0, *element = 0; const int* element_type_array = 0; /* Must evaluate array pattern, so check that this is enabled. */ if (!oskar_station_enable_array_pattern(s)) { *status = OSKAR_ERR_SETTINGS_TELESCOPE; return; } /* Get sized element pattern block (at depth 0). */ element_block = oskar_station_work_beam(work, beam, num_elements * num_points, 0, status); /* Create alias into element block. */ element = oskar_mem_create_alias(element_block, 0, 0, status); /* Loop over elements and evaluate response for each. */ element_type_array = oskar_station_element_types_cpu_const(s); num_element_types = oskar_station_num_element_types(s); for (i = 0; i < num_elements; ++i) { int element_type_idx; element_type_idx = element_type_array[i]; if (element_type_idx >= num_element_types) { *status = OSKAR_ERR_OUT_OF_RANGE; break; } oskar_mem_set_alias(element, element_block, i * num_points, num_points, status); oskar_element_evaluate( oskar_station_element_const(s, element_type_idx), element, oskar_station_element_x_alpha_rad(s, i) + M_PI/2.0, /* FIXME Will change: This matches the old convention. */ oskar_station_element_y_alpha_rad(s, i), num_points, x, y, z, frequency_hz, theta, phi, status); } /* Generate beamforming weights. */ oskar_evaluate_element_weights(weights, weights_error, wavenumber, s, beam_x, beam_y, beam_z, time_index, status); /* Use DFT to evaluate array response. */ oskar_dftw(num_elements, wavenumber, oskar_station_element_true_x_enu_metres_const(s), oskar_station_element_true_y_enu_metres_const(s), oskar_station_element_true_z_enu_metres_const(s), weights, num_points, x, y, (is_3d ? z : 0), element_block, beam, status); /* Free element alias. */ oskar_mem_free(element, status); /* Normalise array response if required. */ if (oskar_station_normalise_array_pattern(s)) oskar_mem_scale_real(beam, 1.0 / num_elements, status); } /* Blank (set to zero) points below the horizon. */ oskar_blank_below_horizon(num_points, z, beam, status); } /* If there are child stations, must first evaluate the beam for each. */ else { int i; oskar_Mem* signal; /* Must evaluate array pattern, so check that this is enabled. */ if (!oskar_station_enable_array_pattern(s)) { *status = OSKAR_ERR_SETTINGS_TELESCOPE; return; } /* Get sized work array for this depth, with the correct type. */ signal = oskar_station_work_beam(work, beam, num_elements * num_points, depth, status); /* Check if child stations are identical. */ if (oskar_station_identical_children(s)) { /* Set up the output buffer for the first station. */ oskar_Mem* output0; output0 = oskar_mem_create_alias(signal, 0, num_points, status); /* Recursive call. */ oskar_evaluate_station_beam_aperture_array_private(output0, oskar_station_child_const(s, 0), num_points, x, y, z, gast, frequency_hz, work, time_index, depth + 1, status); /* Copy beam for child station 0 into memory for other stations. */ for (i = 1; i < num_elements; ++i) { oskar_mem_copy_contents(signal, output0, i * num_points, 0, oskar_mem_length(output0), status); } oskar_mem_free(output0, status); } else { /* Loop over child stations. */ for (i = 0; i < num_elements; ++i) { /* Set up the output buffer for this station. */ oskar_Mem* output; output = oskar_mem_create_alias(signal, i * num_points, num_points, status); /* Recursive call. */ oskar_evaluate_station_beam_aperture_array_private(output, oskar_station_child_const(s, i), num_points, x, y, z, gast, frequency_hz, work, time_index, depth + 1, status); oskar_mem_free(output, status); } } /* Generate beamforming weights and form beam from child stations. */ oskar_evaluate_element_weights(weights, weights_error, wavenumber, s, beam_x, beam_y, beam_z, time_index, status); oskar_dftw(num_elements, wavenumber, oskar_station_element_true_x_enu_metres_const(s), oskar_station_element_true_y_enu_metres_const(s), oskar_station_element_true_z_enu_metres_const(s), weights, num_points, x, y, (is_3d ? z : 0), signal, beam, status); /* Normalise array response if required. */ if (oskar_station_normalise_array_pattern(s)) oskar_mem_scale_real(beam, 1.0 / num_elements, status); } }