Ejemplo n.º 1
0
void oskar_beam_pattern_set_telescope_model(oskar_BeamPattern* h,
        const oskar_Telescope* model, int* status)
{
    if (*status) return;

    /* Check the model is not empty. */
    if (oskar_telescope_num_stations(model) == 0)
    {
        oskar_log_error(h->log, "Telescope model is empty.");
        *status = OSKAR_ERR_SETTINGS_TELESCOPE;
        return;
    }

    /* Remove any existing telescope model, and copy the new one. */
    oskar_telescope_free(h->tel, status);
    h->tel = oskar_telescope_create_copy(model, OSKAR_CPU, status);
    h->pol_mode = oskar_telescope_pol_mode(h->tel);
    h->phase_centre_deg[0] = oskar_telescope_phase_centre_ra_rad(h->tel) *
            180.0 / M_PI;
    h->phase_centre_deg[1] = oskar_telescope_phase_centre_dec_rad(h->tel) *
            180.0 / M_PI;

    /* Analyse the telescope model. */
    oskar_telescope_analyse(h->tel, status);
    if (h->log)
        oskar_telescope_log_summary(h->tel, h->log, status);
}
void oskar_imager_set_num_planes(oskar_Imager* h, int* status)
{
    int i;
    if (*status || h->num_planes > 0) return;
    if (h->num_sel_freqs == 0)
    {
        oskar_log_error(h->log,
                "Input visibility channel frequencies not set.");
        *status = OSKAR_ERR_OUT_OF_RANGE;
        return;
    }

    /* Set image meta-data. */
    h->num_im_channels = h->chan_snaps ? h->num_sel_freqs : 1;
    h->im_freqs = (double*) realloc(h->im_freqs,
            h->num_im_channels * sizeof(double));
    if (h->chan_snaps)
    {
        for (i = 0; i < h->num_sel_freqs; ++i)
            h->im_freqs[i] = h->sel_freqs[i];
    }
    else
    {
        h->im_freqs[0] = 0.0;
        for (i = 0; i < h->num_sel_freqs; ++i)
            h->im_freqs[0] += h->sel_freqs[i];
        h->im_freqs[0] /= h->num_sel_freqs;
    }
    h->num_planes = h->num_im_channels * h->num_im_pols;
}
Ejemplo n.º 3
0
TEST(Log, special_methods)
{
    /*oskar_log_create(OSKAR_LOG_NONE, OSKAR_LOG_DEBUG);*/
    oskar_log_set_term_priority(OSKAR_LOG_DEBUG);
    oskar_log_warning("This is a warning");
    oskar_log_error("This is an error");
    oskar_log_section('M', "This is a section");
    oskar_log_section('W', "This is a warning section");
    oskar_log_section('D', "This is a debug section");
}
Ejemplo n.º 4
0
int main(int argc, char** argv)
{
    int status = 0;
    oskar::OptionParser opt("oskar_convert_geodetic_to_ecef",
            oskar_version_string());
    opt.set_description("Converts geodetic longitude/latitude/altitude to "
            "Cartesian ECEF coordinates. Assumes WGS84 ellipsoid.");
    opt.add_required("input file", "Path to file containing input coordinates. "
            "Angles must be in degrees.");
    if (!opt.check_options(argc, argv))
        return OSKAR_FAIL;
    const char* filename = opt.get_arg();

    // Load the input file.
    oskar_Mem *lon = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, 0, &status);
    oskar_Mem *lat = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, 0, &status);
    oskar_Mem *alt = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, 0, &status);
    size_t num_points = oskar_mem_load_ascii(filename, 3, &status,
            lon, "", lat, "", alt, "0.0");
    oskar_mem_scale_real(lon, M_PI / 180.0, &status);
    oskar_mem_scale_real(lat, M_PI / 180.0, &status);

    // Convert coordinates.
    oskar_Mem *x = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU,
            num_points, &status);
    oskar_Mem *y = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU,
            num_points, &status);
    oskar_Mem *z = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU,
            num_points, &status);
    oskar_convert_geodetic_spherical_to_ecef(num_points,
            oskar_mem_double_const(lon, &status),
            oskar_mem_double_const(lat, &status),
            oskar_mem_double_const(alt, &status),
            oskar_mem_double(x, &status),
            oskar_mem_double(y, &status),
            oskar_mem_double(z, &status));

    // Print converted coordinates.
    oskar_mem_save_ascii(stdout, 3, num_points, &status, x, y, z);

    // Clean up.
    oskar_mem_free(lon, &status);
    oskar_mem_free(lat, &status);
    oskar_mem_free(alt, &status);
    oskar_mem_free(x, &status);
    oskar_mem_free(y, &status);
    oskar_mem_free(z, &status);
    if (status)
    {
        oskar_log_error(0, oskar_get_error_string(status));
        return status;
    }

    return 0;
}
Ejemplo n.º 5
0
TEST(Log, special_methods)
{
    oskar_Log* log = 0;
    /*log = oskar_log_create(OSKAR_LOG_WARNING, OSKAR_LOG_DEBUG);*/
    oskar_log_warning(log, "This is a warning");
    oskar_log_error(log, "This is an error");
    oskar_log_section(log, 'M', "This is a section!");
    oskar_log_section(log, 'W', "This is a warning section!");
    oskar_log_section(log, 'D', "This is a warning section!");
    if (log) oskar_log_free(log);
}
Ejemplo n.º 6
0
void oskar_simulator_check_init(oskar_Simulator* h, int* status)
{
    if (*status) return;

    /* Check that the telescope model has been set. */
    if (!h->tel)
    {
        oskar_log_error(h->log, "Telescope model not set.");
        *status = OSKAR_ERR_SETTINGS_TELESCOPE;
        return;
    }

    /* Create the visibility header if required. */
    if (!h->header)
        set_up_vis_header(h, status);

    /* Calculate source parameters if required. */
    if (!h->init_sky)
    {
        int i, num_failed = 0;
        double ra0, dec0;

        /* Compute source direction cosines relative to phase centre. */
        ra0 = oskar_telescope_phase_centre_ra_rad(h->tel);
        dec0 = oskar_telescope_phase_centre_dec_rad(h->tel);
        for (i = 0; i < h->num_sky_chunks; ++i)
        {
            oskar_sky_evaluate_relative_directions(h->sky_chunks[i],
                    ra0, dec0, status);

            /* Evaluate extended source parameters. */
            oskar_sky_evaluate_gaussian_source_parameters(h->sky_chunks[i],
                    h->zero_failed_gaussians, ra0, dec0, &num_failed, status);
        }
        if (num_failed > 0)
        {
            if (h->zero_failed_gaussians)
                oskar_log_warning(h->log, "Gaussian ellipse solution failed "
                        "for %i sources. These will have their fluxes "
                        "set to zero.", num_failed);
            else
                oskar_log_warning(h->log, "Gaussian ellipse solution failed "
                        "for %i sources. These will be simulated "
                        "as point sources.", num_failed);
        }
        h->init_sky = 1;
    }

    /* Check that each compute device has been set up. */
    set_up_device_data(h, status);
}
void oskar_beam_pattern_check_init(oskar_BeamPattern* h, int* status)
{
    if (*status) return;

    /* Check that the telescope model has been set. */
    if (!h->tel)
    {
        oskar_log_error(h->log, "Telescope model not set.");
        *status = OSKAR_ERR_SETTINGS_TELESCOPE;
        return;
    }

    /* Check that each compute device has been set up. */
    set_up_host_data(h, status);
    set_up_device_data(h, status);
}
int main(int argc, char** argv)
{
    int error = 0;

    oskar::OptionParser opt("oskar_fits_image_to_sky_model",
            oskar_version_string());
    opt.set_description("Converts a FITS image to an OSKAR sky model. A number "
            "of options are provided to control how much of the image is used "
            "to make the sky model.");
    opt.add_required("FITS file", "The input FITS image to convert.");
    opt.add_required("sky model file", "The output OSKAR sky model file name to save.");
    opt.add_flag("-s", "Spectral index. This is the spectral index that will "
            "be given to each pixel in the output sky model.", 1, "0.0");
    opt.add_flag("-f", "Minimum allowed fraction of image peak. Pixel values "
            "below this fraction will be ignored.", 1, "0.0");
    opt.add_flag("-n", "Noise floor in units of original image. "
            "Pixels below this value will be ignored.", 1, "0.0");
    if (!opt.check_options(argc, argv)) return EXIT_FAILURE;

    // Parse command line.
    double spectral_index = 0.0;
    double min_peak_fraction = 0.0;
    double min_abs_val = 0.0;
    opt.get("-f")->getDouble(min_peak_fraction);
    opt.get("-n")->getDouble(min_abs_val);
    opt.get("-s")->getDouble(spectral_index);

    // Load the FITS image data.
    oskar_Sky* sky = oskar_sky_from_fits_file(OSKAR_DOUBLE, opt.get_arg(0),
            min_peak_fraction, min_abs_val, "Jy/beam", 0, 0.0, spectral_index,
            &error);

    // Write out the sky model.
    oskar_sky_save(opt.get_arg(1), sky, &error);
    if (error)
    {
        oskar_log_error(0, oskar_get_error_string(error));
        return error;
    }

    oskar_sky_free(sky, &error);
    return 0;
}
Ejemplo n.º 9
0
void oskar_simulator_set_gpus(oskar_Simulator* h, int num,
        int* ids, int* status)
{
    int i, num_gpus_avail;
    if (*status) return;
    free_device_data(h, status);
    num_gpus_avail = oskar_device_count(status);
    if (*status) return;
    if (num < 0)
    {
        h->num_gpus = num_gpus_avail;
        h->gpu_ids = (int*) realloc(h->gpu_ids, h->num_gpus * sizeof(int));
        for (i = 0; i < h->num_gpus; ++i)
            h->gpu_ids[i] = i;
    }
    else if (num > 0)
    {
        if (num > num_gpus_avail)
        {
            oskar_log_error(h->log, "More GPUs were requested than found.");
            *status = OSKAR_ERR_COMPUTE_DEVICES;
            return;
        }
        h->num_gpus = num;
        h->gpu_ids = (int*) realloc(h->gpu_ids, h->num_gpus * sizeof(int));
        for (i = 0; i < h->num_gpus; ++i)
            h->gpu_ids[i] = ids[i];
    }
    else /* num == 0 */
    {
        free(h->gpu_ids);
        h->gpu_ids = 0;
        h->num_gpus = 0;
    }
    for (i = 0; i < h->num_gpus; ++i)
    {
        oskar_device_set(h->gpu_ids[i], status);
        if (*status) return;
    }
}
int main(int argc, char** argv)
{
    oskar::OptionParser opt("oskar_cuda_system_info", oskar_version_string());
    opt.set_description("Display a summary of the available CUDA capability");
    if (!opt.check_options(argc, argv)) return EXIT_FAILURE;

    // Create the CUDA info structure.
    int error = 0;
    oskar_CudaInfo* info = oskar_cuda_info_create(&error);
    if (error)
    {
        oskar_log_error(0, "Could not determine CUDA system information (%s)",
                oskar_get_error_string(error));
        oskar_cuda_info_free(info);
        return error;
    }

    // Log the CUDA system info.
    oskar_cuda_info_log(NULL, info);
    oskar_cuda_info_free(info);
    return 0;
}
Ejemplo n.º 11
0
void oskar_simulator_set_telescope_model(oskar_Simulator* h,
        const oskar_Telescope* model, int* status)
{
    if (*status) return;

    /* Check the model is not empty. */
    if (oskar_telescope_num_stations(model) == 0)
    {
        oskar_log_error(h->log, "Telescope model is empty.");
        *status = OSKAR_ERR_SETTINGS_TELESCOPE;
        return;
    }

    /* Remove any existing telescope model, and copy the new one. */
    oskar_telescope_free(h->tel, status);
    h->tel = oskar_telescope_create_copy(model, OSKAR_CPU, status);

    /* Analyse the telescope model. */
    oskar_telescope_analyse(h->tel, status);
    if (h->log)
        oskar_telescope_log_summary(h->tel, h->log, status);
}
static void set_up_host_data(oskar_BeamPattern* h, int *status)
{
    int i, k;
    size_t j;
    if (*status) return;

    /* Set up pixel positions. */
    oskar_beam_pattern_generate_coordinates(h,
            OSKAR_SPHERICAL_TYPE_EQUATORIAL, status);

    /* Work out how many pixel chunks have to be processed. */
    h->num_chunks = (h->num_pixels + h->max_chunk_size - 1) / h->max_chunk_size;

    /* Create scratch arrays for output pixel data. */
    if (!h->pix)
    {
        h->pix = oskar_mem_create(h->prec, OSKAR_CPU,
                h->max_chunk_size, status);
        h->ctemp = oskar_mem_create(h->prec | OSKAR_COMPLEX, OSKAR_CPU,
                h->max_chunk_size, status);
    }

    /* Get the contents of the log at this point so we can write a
     * reasonable file header. Replace newlines with zeros. */
    h->settings_log_length = 0;
    free(h->settings_log);
    h->settings_log = oskar_log_file_data(h->log, &h->settings_log_length);
    for (j = 0; j < h->settings_log_length; ++j)
    {
        if (h->settings_log[j] == '\n') h->settings_log[j] = 0;
        if (h->settings_log[j] == '\r') h->settings_log[j] = ' ';
    }

    /* Return if data products already exist. */
    if (h->data_products) return;

    /* Create a file for each requested data product. */
    /* Voltage amplitude and phase can only be generated if there is
     * no averaging. */
    if (h->separate_time_and_channel)
    {
        /* Create station-level data products. */
        for (i = 0; i < h->num_active_stations; ++i)
        {
            /* Text file. */
            if (h->voltage_raw_txt)
                new_text_file(h, RAW_COMPLEX, -1, -1, i, 0, 0, status);
            if (h->voltage_amp_txt)
            {
                if (h->pol_mode == OSKAR_POL_MODE_SCALAR)
                    new_text_file(h, AMP, -1, -1, i, 0, 0, status);
                else for (k = XX; k <= YY; ++k)
                    new_text_file(h, AMP, -1, k, i, 0, 0, status);
            }
            if (h->voltage_phase_txt)
            {
                if (h->pol_mode == OSKAR_POL_MODE_SCALAR)
                    new_text_file(h, PHASE, -1, -1, i, 0, 0, status);
                else for (k = XX; k <= YY; ++k)
                    new_text_file(h, PHASE, -1, k, i, 0, 0, status);
            }
            if (h->ixr_txt && h->pol_mode == OSKAR_POL_MODE_FULL)
                new_text_file(h, IXR, -1, -1, i, 0, 0, status);

            /* Can only create images if coordinates are on a grid. */
            if (h->coord_grid_type != 'B') continue;

            /* FITS file. */
            if (h->voltage_amp_fits)
            {
                if (h->pol_mode == OSKAR_POL_MODE_SCALAR)
                    new_fits_file(h, AMP, -1, -1, i, 0, 0, status);
                else for (k = XX; k <= YY; ++k)
                    new_fits_file(h, AMP, -1, k, i, 0, 0, status);
            }
            if (h->voltage_phase_fits)
            {
                if (h->pol_mode == OSKAR_POL_MODE_SCALAR)
                    new_fits_file(h, PHASE, -1, -1, i, 0, 0, status);
                else for (k = XX; k <= YY; ++k)
                    new_fits_file(h, PHASE, -1, k, i, 0, 0, status);
            }
            if (h->ixr_fits && h->pol_mode == OSKAR_POL_MODE_FULL)
                new_fits_file(h, IXR, -1, -1, i, 0, 0, status);
        }
    }

    /* Create data products that can be averaged. */
    if (h->separate_time_and_channel)
        create_averaged_products(h, 0, 0, status);
    if (h->average_time_and_channel)
        create_averaged_products(h, 1, 1, status);
    if (h->average_single_axis == 'C')
        create_averaged_products(h, 0, 1, status);
    else if (h->average_single_axis == 'T')
        create_averaged_products(h, 1, 0, status);

    /* Check that at least one output file will be generated. */
    if (h->num_data_products == 0 && !*status)
    {
        *status = OSKAR_ERR_FILE_IO;
        oskar_log_error(h->log, "No output file(s) selected.");
    }
}
static void set_up_device_data(oskar_BeamPattern* h, int* status)
{
    int i, beam_type, max_src, max_size, auto_power, cross_power, raw_data;
    if (*status) return;

    /* Get local variables. */
    max_src = h->max_chunk_size;
    max_size = h->num_active_stations * max_src;
    beam_type = h->prec | OSKAR_COMPLEX;
    if (h->pol_mode == OSKAR_POL_MODE_FULL)
        beam_type |= OSKAR_MATRIX;
    raw_data = h->ixr_txt || h->ixr_fits ||
            h->voltage_raw_txt || h->voltage_amp_txt || h->voltage_phase_txt ||
            h->voltage_amp_fits || h->voltage_phase_fits;
    auto_power = h->auto_power_fits || h->auto_power_txt;
    cross_power = h->cross_power_raw_txt ||
            h->cross_power_amp_fits || h->cross_power_phase_fits ||
            h->cross_power_amp_txt || h->cross_power_phase_txt;

    /* Expand the number of devices to the number of selected GPUs,
     * if required. */
    if (h->num_devices < h->num_gpus)
        oskar_beam_pattern_set_num_devices(h, h->num_gpus);

    for (i = 0; i < h->num_devices; ++i)
    {
        int dev_loc, i_stokes;
        DeviceData* d = &h->d[i];
        if (*status) break;

        /* Select the device. */
        if (i < h->num_gpus)
        {
            oskar_device_set(h->gpu_ids[i], status);
            dev_loc = OSKAR_GPU;
        }
        else
        {
            dev_loc = OSKAR_CPU;
        }

        /* Device memory. */
        d->previous_chunk_index = -1;
        if (!d->tel)
        {
            d->jones_data = oskar_mem_create(beam_type, dev_loc, max_size,
                    status);
            d->x    = oskar_mem_create(h->prec, dev_loc, 1 + max_src, status);
            d->y    = oskar_mem_create(h->prec, dev_loc, 1 + max_src, status);
            d->z    = oskar_mem_create(h->prec, dev_loc, 1 + max_src, status);
            d->tel  = oskar_telescope_create_copy(h->tel, dev_loc, status);
            d->work = oskar_station_work_create(h->prec, dev_loc, status);
        }

        /* Host memory. */
        if (!d->jones_data_cpu[0] && raw_data)
        {
            d->jones_data_cpu[0] = oskar_mem_create(beam_type, OSKAR_CPU,
                    max_size, status);
            d->jones_data_cpu[1] = oskar_mem_create(beam_type, OSKAR_CPU,
                    max_size, status);
        }

        /* Auto-correlation beam output arrays. */
        for (i_stokes = 0; i_stokes < 4; ++i_stokes)
        {
            if (!h->stokes[i_stokes]) continue;

            if (!d->auto_power[i_stokes] && auto_power)
            {
                /* Device memory. */
                d->auto_power[i_stokes] = oskar_mem_create(beam_type, dev_loc,
                        max_size, status);

                /* Host memory. */
                d->auto_power_cpu[i_stokes][0] = oskar_mem_create(
                        beam_type, OSKAR_CPU, max_size, status);
                d->auto_power_cpu[i_stokes][1] = oskar_mem_create(
                        beam_type, OSKAR_CPU, max_size, status);
                if (h->average_single_axis == 'T')
                    d->auto_power_time_avg[i_stokes] = oskar_mem_create(
                            beam_type, OSKAR_CPU, max_size, status);
                if (h->average_single_axis == 'C')
                    d->auto_power_channel_avg[i_stokes] = oskar_mem_create(
                            beam_type, OSKAR_CPU, max_size, status);
                if (h->average_time_and_channel)
                    d->auto_power_channel_and_time_avg[i_stokes] =
                            oskar_mem_create(beam_type, OSKAR_CPU,
                                    max_size, status);
            }

            /* Cross-correlation beam output arrays. */
            if (!d->cross_power[i_stokes] && cross_power)
            {
                if (h->num_active_stations < 2)
                {
                    oskar_log_error(h->log, "Cannot create cross-power beam "
                            "using less than two active stations.");
                    *status = OSKAR_ERR_INVALID_ARGUMENT;
                    break;
                }

                /* Device memory. */
                d->cross_power[i_stokes] = oskar_mem_create(
                        beam_type, dev_loc, max_src, status);

                /* Host memory. */
                d->cross_power_cpu[i_stokes][0] = oskar_mem_create(

                        beam_type, OSKAR_CPU, max_src, status);
                d->cross_power_cpu[i_stokes][1] = oskar_mem_create(
                        beam_type, OSKAR_CPU, max_src, status);
                if (h->average_single_axis == 'T')
                    d->cross_power_time_avg[i_stokes] = oskar_mem_create(
                            beam_type, OSKAR_CPU, max_src, status);
                if (h->average_single_axis == 'C')
                    d->cross_power_channel_avg[i_stokes] = oskar_mem_create(
                            beam_type, OSKAR_CPU, max_src, status);
                if (h->average_time_and_channel)
                    d->cross_power_channel_and_time_avg[i_stokes] =
                            oskar_mem_create(beam_type, OSKAR_CPU,
                                    max_src, status);
            }
            if (d->auto_power[i_stokes])
                oskar_mem_clear_contents(d->auto_power[i_stokes], status);
            if (d->cross_power[i_stokes])
                oskar_mem_clear_contents(d->cross_power[i_stokes], status);
        }

        /* Timers. */
        if (!d->tmr_compute)
            d->tmr_compute = oskar_timer_create(OSKAR_TIMER_NATIVE);
    }
}
Ejemplo n.º 14
0
TEST(Log, oskar_log_error)
{
    oskar_log_error("This is an error");
}
void oskar_convert_brightness_to_jy(oskar_Mem* data, double beam_area_pixels,
        double pixel_area_sr, double frequency_hz, double min_peak_fraction,
        double min_abs_val, const char* reported_map_units,
        const char* default_map_units, int override_input_units, int* status)
{
    static const double k_B = 1.3806488e-23; /* Boltzmann constant. */
    double lambda, peak = 0.0, peak_min = 0.0, scaling = 1.0, val = 0.0;
    const char* unit_str = 0;
    int i = 0, num_pixels, units = 0, type;
    if (*status) return;

    /* Filter and find peak of image. */
    num_pixels = (int) oskar_mem_length(data);
    type = oskar_mem_precision(data);
    if (type == OSKAR_SINGLE)
    {
        float *img = oskar_mem_float(data, status);
        for (i = 0; i < num_pixels; ++i)
        {
            val = img[i];
            if (val > peak) peak = val;
        }
        peak_min = peak * min_peak_fraction;
        for (i = 0; i < num_pixels; ++i)
        {
            val = img[i];
            if (val < peak_min || val < min_abs_val) img[i] = 0.0;
        }
    }
    else
    {
        double *img = oskar_mem_double(data, status);
        for (i = 0; i < num_pixels; ++i)
        {
            val = img[i];
            if (val > peak) peak = val;
        }
        peak_min = peak * min_peak_fraction;
        for (i = 0; i < num_pixels; ++i)
        {
            val = img[i];
            if (val < peak_min || val < min_abs_val) img[i] = 0.0;
        }
    }

    /* Find brightness units. */
    unit_str = (!reported_map_units || override_input_units) ?
            default_map_units : reported_map_units;
    if (!strncmp(unit_str, "JY/BEAM", 7) ||
            !strncmp(unit_str, "Jy/beam", 7))
        units = JY_BEAM;
    else if (!strncmp(unit_str, "JY/PIXEL", 8) ||
            !strncmp(unit_str, "Jy/pixel", 8))
        units = JY_PIXEL;
    else if (!strncmp(unit_str, "mK", 2))
        units = MK;
    else if (!strncmp(unit_str, "K", 1))
        units = K;
    else
        *status = OSKAR_ERR_BAD_UNITS;

    /* Check if we need to convert the pixel values. */
    if (units == JY_BEAM)
    {
        if (beam_area_pixels == 0.0)
        {
            oskar_log_error("Need beam area for maps in Jy/beam.");
            *status = OSKAR_ERR_BAD_UNITS;
        }
        else
            scaling = 1.0 / beam_area_pixels;
    }
    else if (units == K || units == MK)
    {
        if (units == MK)
            scaling = 1e-3; /* Convert milli-Kelvin to Kelvin. */

        /* Convert temperature to Jansky per pixel. */
        /* Brightness temperature to flux density conversion:
         * http://www.iram.fr/IRAMFR/IS/IS2002/html_1/node187.html */
        /* Multiply by 2.0 * k_B * pixel_area * 10^26 / lambda^2. */
        lambda = 299792458.0 / frequency_hz;
        scaling *= (2.0 * k_B * pixel_area_sr * 1e26 / (lambda*lambda));
    }

    /* Scale pixels into Jy. */
    if (scaling != 1.0)
        oskar_mem_scale_real(data, scaling, 0, oskar_mem_length(data), status);
}
Ejemplo n.º 16
0
void oskar_simulator_run(oskar_Simulator* h, int* status)
{
    int i, num_threads = 1, num_vis_blocks;
    if (*status) return;

    /* Check the visibilities are going somewhere. */
    if (!h->vis_name
#ifndef OSKAR_NO_MS
            && !h->ms_name
#endif
    )
    {
        oskar_log_error(h->log, "No output file specified.");
#ifdef OSKAR_NO_MS
        if (h->ms_name)
            oskar_log_error(h->log,
                    "OSKAR was compiled without Measurement Set support.");
#endif
        *status = OSKAR_ERR_FILE_IO;
        return;
    }

    /* Initialise if required. */
    oskar_simulator_check_init(h, status);

    /* Get the number of visibility blocks to be processed. */
    num_vis_blocks = oskar_simulator_num_vis_blocks(h);

    /* Record memory usage. */
    if (h->log && !*status)
    {
        oskar_log_section(h->log, 'M', "Initial memory usage");
#ifdef OSKAR_HAVE_CUDA
        for (i = 0; i < h->num_gpus; ++i)
            oskar_cuda_mem_log(h->log, 0, h->gpu_ids[i]);
#endif
        system_mem_log(h->log);
        oskar_log_section(h->log, 'M', "Starting simulation...");
    }

    /* Start simulation timer. */
    oskar_timer_start(h->tmr_sim);

    /*-----------------------------------------------------------------------
     *-- START OF MULTITHREADED SIMULATION CODE -----------------------------
     *-----------------------------------------------------------------------*/
    /* Loop over blocks of observation time, running simulation and file
     * writing one block at a time. Simulation and file output are overlapped
     * by using double buffering, and a dedicated thread is used for file
     * output.
     *
     * Thread 0 is used for file writes.
     * Threads 1 to n (mapped to compute devices) do the simulation.
     *
     * Note that no write is launched on the first loop counter (as no
     * data are ready yet) and no simulation is performed for the last loop
     * counter (which corresponds to the last block + 1) as this iteration
     * simply writes the last block.
     */
#ifdef _OPENMP
    num_threads = h->num_devices + 1;
    omp_set_num_threads(num_threads);
    omp_set_nested(0);
#else
    oskar_log_warning(h->log, "OpenMP not found: Using one compute device.");
#endif

    oskar_simulator_reset_work_unit_index(h);
#pragma omp parallel
    {
        int b, thread_id = 0, device_id = 0;

        /* Get host thread ID and device ID. */
#ifdef _OPENMP
        thread_id = omp_get_thread_num();
        device_id = thread_id - 1;
#endif

        /* Loop over simulation time blocks (+1, for the last write). */
        for (b = 0; b < num_vis_blocks + 1; ++b)
        {
            if ((thread_id > 0 || num_threads == 1) && b < num_vis_blocks)
                oskar_simulator_run_block(h, b, device_id, status);
            if (thread_id == 0 && b > 0)
            {
                oskar_VisBlock* block;
                block = oskar_simulator_finalise_block(h, b - 1, status);
                oskar_simulator_write_block(h, block, b - 1, status);
            }

            /* Barrier 1: Reset work unit index. */
#pragma omp barrier
            if (thread_id == 0)
                oskar_simulator_reset_work_unit_index(h);

            /* Barrier 2: Synchronise before moving to the next block. */
#pragma omp barrier
            if (thread_id == 0 && b < num_vis_blocks && h->log && !*status)
                oskar_log_message(h->log, 'S', 0, "Block %*i/%i (%3.0f%%) "
                        "complete. Simulation time elapsed: %.3f s",
                        disp_width(num_vis_blocks), b+1, num_vis_blocks,
                        100.0 * (b+1) / (double)num_vis_blocks,
                        oskar_timer_elapsed(h->tmr_sim));
        }
    }
    /*-----------------------------------------------------------------------
     *-- END OF MULTITHREADED SIMULATION CODE -------------------------------
     *-----------------------------------------------------------------------*/

    /* Record memory usage. */
    if (h->log && !*status)
    {
        oskar_log_section(h->log, 'M', "Final memory usage");
#ifdef OSKAR_HAVE_CUDA
        for (i = 0; i < h->num_gpus; ++i)
            oskar_cuda_mem_log(h->log, 0, h->gpu_ids[i]);
#endif
        system_mem_log(h->log);
    }

    /* If there are sources in the simulation and the station beam is not
     * normalised to 1.0 at the phase centre, the values of noise RMS
     * may give a very unexpected S/N ratio!
     * The alternative would be to scale the noise to match the station
     * beam gain but that would require knowledge of the station beam
     * amplitude at the phase centre for each time and channel. */
    if (h->log && oskar_telescope_noise_enabled(h->tel) && !*status)
    {
        int have_sources, amp_calibrated;
        have_sources = (h->num_sky_chunks > 0 &&
                oskar_sky_num_sources(h->sky_chunks[0]) > 0);
        amp_calibrated = oskar_station_normalise_final_beam(
                oskar_telescope_station_const(h->tel, 0));
        if (have_sources && !amp_calibrated)
        {
            const char* a = "WARNING: System noise added to visibilities";
            const char* b = "without station beam normalisation enabled.";
            const char* c = "This will give an invalid signal to noise ratio.";
            oskar_log_line(h->log, 'W', ' '); oskar_log_line(h->log, 'W', '*');
            oskar_log_message(h->log, 'W', -1, a);
            oskar_log_message(h->log, 'W', -1, b);
            oskar_log_message(h->log, 'W', -1, c);
            oskar_log_line(h->log, 'W', '*'); oskar_log_line(h->log, 'W', ' ');
        }
    }

    /* Record times and summarise output files. */
    if (h->log && !*status)
    {
        size_t log_size = 0;
        char* log_data;
        oskar_log_set_value_width(h->log, 25);
        record_timing(h);
        oskar_log_section(h->log, 'M', "Simulation complete");
        oskar_log_message(h->log, 'M', 0, "Output(s):");
        if (h->vis_name)
            oskar_log_value(h->log, 'M', 1,
                    "OSKAR binary file", "%s", h->vis_name);
        if (h->ms_name)
            oskar_log_value(h->log, 'M', 1,
                    "Measurement Set", "%s", h->ms_name);

        /* Write simulation log to the output files. */
        log_data = oskar_log_file_data(h->log, &log_size);
#ifndef OSKAR_NO_MS
        if (h->ms)
            oskar_ms_add_history(h->ms, "OSKAR_LOG", log_data, log_size);
#endif
        if (h->vis)
            oskar_binary_write(h->vis, OSKAR_CHAR, OSKAR_TAG_GROUP_RUN,
                    OSKAR_TAG_RUN_LOG, 0, log_size, log_data, status);
        free(log_data);
    }

    /* Finalise. */
    oskar_simulator_finalise(h, status);
}
Ejemplo n.º 17
0
void oskar_simulator_run_block(oskar_Simulator* h, int block_index,
        int device_id, int* status)
{
    double obs_start_mjd, dt_dump_days;
    int i_active, time_index_start, time_index_end;
    int num_channels, num_times_block, total_chunks, total_times;
    DeviceData* d;
    if (*status) return;

    /* Check that initialisation has happened. We can't initialise here,
     * as we're already multi-threaded at this point. */
    if (!h->header)
    {
        *status = OSKAR_ERR_MEMORY_NOT_ALLOCATED;
        oskar_log_error(h->log, "Simulator not initalised. "
                "Call oskar_simulator_check_init() first.");
        return;
    }

#ifdef _OPENMP
    if (!h->coords_only)
    {
        /* Disable any nested parallelism. */
        omp_set_num_threads(1);
        omp_set_nested(0);
    }
#endif

    /* Set the GPU to use. (Supposed to be a very low-overhead call.) */
    if (device_id >= 0 && device_id < h->num_gpus)
        oskar_device_set(h->gpu_ids[device_id], status);

    /* Clear the visibility block. */
    i_active = block_index % 2; /* Index of the active buffer. */
    d = &(h->d[device_id]);
    oskar_timer_resume(d->tmr_compute);
    oskar_vis_block_clear(d->vis_block, status);

    /* Set the visibility block meta-data. */
    total_chunks = h->num_sky_chunks;
    num_channels = h->num_channels;
    total_times = h->num_time_steps;
    obs_start_mjd = h->time_start_mjd_utc;
    dt_dump_days = h->time_inc_sec / 86400.0;
    time_index_start = block_index * h->max_times_per_block;
    time_index_end = time_index_start + h->max_times_per_block - 1;
    if (time_index_end >= total_times)
        time_index_end = total_times - 1;
    num_times_block = 1 + time_index_end - time_index_start;

    /* Set the number of active times in the block. */
    oskar_vis_block_set_num_times(d->vis_block, num_times_block, status);
    oskar_vis_block_set_start_time_index(d->vis_block, time_index_start);

    /* Go though all possible work units in the block. A work unit is defined
     * as the simulation for one time and one sky chunk. */
    while (!h->coords_only)
    {
        oskar_Sky* sky;
        int i_work_unit, i_chunk, i_time, i_channel, sim_time_idx;

        oskar_mutex_lock(h->mutex);
        i_work_unit = (h->work_unit_index)++;
        oskar_mutex_unlock(h->mutex);
        if ((i_work_unit >= num_times_block * total_chunks) || *status) break;

        /* Convert slice index to chunk/time index. */
        i_chunk      = i_work_unit / num_times_block;
        i_time       = i_work_unit - i_chunk * num_times_block;
        sim_time_idx = time_index_start + i_time;

        /* Copy sky chunk to device only if different from the previous one. */
        if (i_chunk != d->previous_chunk_index)
        {
            oskar_timer_resume(d->tmr_copy);
            oskar_sky_copy(d->chunk, h->sky_chunks[i_chunk], status);
            oskar_timer_pause(d->tmr_copy);
        }
        sky = h->apply_horizon_clip ? d->chunk_clip : d->chunk;

        /* Apply horizon clip if required. */
        if (h->apply_horizon_clip)
        {
            double gast, mjd;
            mjd = obs_start_mjd + dt_dump_days * (sim_time_idx + 0.5);
            gast = oskar_convert_mjd_to_gast_fast(mjd);
            oskar_timer_resume(d->tmr_clip);
            oskar_sky_horizon_clip(d->chunk_clip, d->chunk, d->tel, gast,
                    d->station_work, status);
            oskar_timer_pause(d->tmr_clip);
        }

        /* Simulate all baselines for all channels for this time and chunk. */
        for (i_channel = 0; i_channel < num_channels; ++i_channel)
        {
            if (*status) break;
            if (h->log)
            {
                oskar_mutex_lock(h->mutex);
                oskar_log_message(h->log, 'S', 1, "Time %*i/%i, "
                        "Chunk %*i/%i, Channel %*i/%i [Device %i, %i sources]",
                        disp_width(total_times), sim_time_idx + 1, total_times,
                        disp_width(total_chunks), i_chunk + 1, total_chunks,
                        disp_width(num_channels), i_channel + 1, num_channels,
                        device_id, oskar_sky_num_sources(sky));
                oskar_mutex_unlock(h->mutex);
            }
            sim_baselines(h, d, sky, i_channel, i_time, sim_time_idx, status);
        }
        d->previous_chunk_index = i_chunk;
    }

    /* Copy the visibility block to host memory. */
    oskar_timer_resume(d->tmr_copy);
    oskar_vis_block_copy(d->vis_block_cpu[i_active], d->vis_block, status);
    oskar_timer_pause(d->tmr_copy);
    oskar_timer_pause(d->tmr_compute);
}
Ejemplo n.º 18
0
void oskar_interferometer_run(oskar_Interferometer* h, int* status)
{
    int i, num_threads;
    oskar_Thread** threads = 0;
    ThreadArgs* args = 0;
    if (*status || !h) return;

    /* Check the visibilities are going somewhere. */
    if (!h->vis_name
#ifndef OSKAR_NO_MS
            && !h->ms_name
#endif
    )
    {
        oskar_log_error(h->log, "No output file specified.");
#ifdef OSKAR_NO_MS
        if (h->ms_name)
            oskar_log_error(h->log,
                    "OSKAR was compiled without Measurement Set support.");
#endif
        *status = OSKAR_ERR_FILE_IO;
        return;
    }

    /* Initialise if required. */
    oskar_interferometer_check_init(h, status);

    /* Set up worker threads. */
    num_threads = h->num_devices + 1;
    oskar_barrier_set_num_threads(h->barrier, num_threads);
    threads = (oskar_Thread**) calloc(num_threads, sizeof(oskar_Thread*));
    args = (ThreadArgs*) calloc(num_threads, sizeof(ThreadArgs));
    for (i = 0; i < num_threads; ++i)
    {
        args[i].h = h;
        args[i].num_threads = num_threads;
        args[i].thread_id = i;
    }

    /* Record memory usage. */
    if (h->log && !*status)
    {
        oskar_log_section(h->log, 'M', "Initial memory usage");
#ifdef OSKAR_HAVE_CUDA
        for (i = 0; i < h->num_gpus; ++i)
            oskar_cuda_mem_log(h->log, 0, h->gpu_ids[i]);
#endif
        system_mem_log(h->log);
        oskar_log_section(h->log, 'M', "Starting simulation...");
    }

    /* Start simulation timer. */
    oskar_timer_start(h->tmr_sim);

    /* Set status code. */
    h->status = *status;

    /* Start the worker threads. */
    oskar_interferometer_reset_work_unit_index(h);
    for (i = 0; i < num_threads; ++i)
        threads[i] = oskar_thread_create(run_blocks, (void*)&args[i], 0);

    /* Wait for worker threads to finish. */
    for (i = 0; i < num_threads; ++i)
    {
        oskar_thread_join(threads[i]);
        oskar_thread_free(threads[i]);
    }
    free(threads);
    free(args);

    /* Get status code. */
    *status = h->status;

    /* Record memory usage. */
    if (h->log && !*status)
    {
        oskar_log_section(h->log, 'M', "Final memory usage");
#ifdef OSKAR_HAVE_CUDA
        for (i = 0; i < h->num_gpus; ++i)
            oskar_cuda_mem_log(h->log, 0, h->gpu_ids[i]);
#endif
        system_mem_log(h->log);
    }

    /* If there are sources in the simulation and the station beam is not
     * normalised to 1.0 at the phase centre, the values of noise RMS
     * may give a very unexpected S/N ratio!
     * The alternative would be to scale the noise to match the station
     * beam gain but that would require knowledge of the station beam
     * amplitude at the phase centre for each time and channel. */
    if (h->log && oskar_telescope_noise_enabled(h->tel) && !*status)
    {
        int have_sources, amp_calibrated;
        have_sources = (h->num_sky_chunks > 0 &&
                oskar_sky_num_sources(h->sky_chunks[0]) > 0);
        amp_calibrated = oskar_station_normalise_final_beam(
                oskar_telescope_station_const(h->tel, 0));
        if (have_sources && !amp_calibrated)
        {
            const char* a = "WARNING: System noise added to visibilities";
            const char* b = "without station beam normalisation enabled.";
            const char* c = "This will give an invalid signal to noise ratio.";
            oskar_log_line(h->log, 'W', ' '); oskar_log_line(h->log, 'W', '*');
            oskar_log_message(h->log, 'W', -1, a);
            oskar_log_message(h->log, 'W', -1, b);
            oskar_log_message(h->log, 'W', -1, c);
            oskar_log_line(h->log, 'W', '*'); oskar_log_line(h->log, 'W', ' ');
        }
    }

    /* Record times and summarise output files. */
    if (h->log && !*status)
    {
        size_t log_size = 0;
        char* log_data;
        oskar_log_set_value_width(h->log, 25);
        record_timing(h);
        oskar_log_section(h->log, 'M', "Simulation complete");
        oskar_log_message(h->log, 'M', 0, "Output(s):");
        if (h->vis_name)
            oskar_log_value(h->log, 'M', 1,
                    "OSKAR binary file", "%s", h->vis_name);
        if (h->ms_name)
            oskar_log_value(h->log, 'M', 1,
                    "Measurement Set", "%s", h->ms_name);

        /* Write simulation log to the output files. */
        log_data = oskar_log_file_data(h->log, &log_size);
#ifndef OSKAR_NO_MS
        if (h->ms)
            oskar_ms_add_history(h->ms, "OSKAR_LOG", log_data, log_size);
#endif
        if (h->vis)
            oskar_binary_write(h->vis, OSKAR_CHAR, OSKAR_TAG_GROUP_RUN,
                    OSKAR_TAG_RUN_LOG, 0, log_size, log_data, status);
        free(log_data);
    }

    /* Finalise. */
    oskar_interferometer_finalise(h, status);
}
void oskar_imager_read_coords_ms(oskar_Imager* h, const char* filename,
        int i_file, int num_files, int* percent_done, int* percent_next,
        int* status)
{
#ifndef OSKAR_NO_MS
    oskar_MeasurementSet* ms;
    oskar_Mem *uvw, *u, *v, *w, *weight, *time_centroid;
    int num_channels, num_stations, num_baselines, num_pols;
    int start_row, num_rows;
    double *uvw_, *u_, *v_, *w_;
    if (*status) return;

    /* Read the header. */
    ms = oskar_ms_open(filename);
    if (!ms)
    {
        *status = OSKAR_ERR_FILE_IO;
        return;
    }
    num_rows = (int) oskar_ms_num_rows(ms);
    num_stations = (int) oskar_ms_num_stations(ms);
    num_baselines = num_stations * (num_stations - 1) / 2;
    num_pols = (int) oskar_ms_num_pols(ms);
    num_channels = (int) oskar_ms_num_channels(ms);

    /* Set visibility meta-data. */
    oskar_imager_set_vis_frequency(h,
            oskar_ms_freq_start_hz(ms),
            oskar_ms_freq_inc_hz(ms), num_channels);
    oskar_imager_set_vis_phase_centre(h,
            oskar_ms_phase_centre_ra_rad(ms) * 180/M_PI,
            oskar_ms_phase_centre_dec_rad(ms) * 180/M_PI);

    /* Create arrays. */
    uvw = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, 3 * num_baselines, status);
    u = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, num_baselines, status);
    v = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, num_baselines, status);
    w = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, num_baselines, status);
    weight = oskar_mem_create(OSKAR_SINGLE, OSKAR_CPU,
            num_baselines * num_pols, status);
    time_centroid = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, num_baselines,
            status);
    uvw_ = oskar_mem_double(uvw, status);
    u_ = oskar_mem_double(u, status);
    v_ = oskar_mem_double(v, status);
    w_ = oskar_mem_double(w, status);

    /* Loop over visibility blocks. */
    for (start_row = 0; start_row < num_rows; start_row += num_baselines)
    {
        int i, block_size;
        size_t allocated, required;
        if (*status) break;

        /* Read coordinates and weights from Measurement Set. */
        oskar_timer_resume(h->tmr_read);
        block_size = num_rows - start_row;
        if (block_size > num_baselines) block_size = num_baselines;
        allocated = oskar_mem_length(uvw) *
                oskar_mem_element_size(oskar_mem_type(uvw));
        oskar_ms_read_column(ms, "UVW", start_row, block_size,
                allocated, oskar_mem_void(uvw), &required, status);
        allocated = oskar_mem_length(weight) *
                oskar_mem_element_size(oskar_mem_type(weight));
        oskar_ms_read_column(ms, "WEIGHT", start_row, block_size,
                allocated, oskar_mem_void(weight), &required, status);
        allocated = oskar_mem_length(time_centroid) *
                oskar_mem_element_size(oskar_mem_type(time_centroid));
        oskar_ms_read_column(ms, "TIME_CENTROID", start_row, block_size,
                allocated, oskar_mem_void(time_centroid), &required, status);

        /* Split up baseline coordinates. */
        for (i = 0; i < block_size; ++i)
        {
            u_[i] = uvw_[3*i + 0];
            v_[i] = uvw_[3*i + 1];
            w_[i] = uvw_[3*i + 2];
        }

        /* Update the imager with the data. */
        oskar_timer_pause(h->tmr_read);
        oskar_imager_update(h, block_size, 0, num_channels - 1, num_pols,
                u, v, w, 0, weight, time_centroid, status);
        *percent_done = (int) round(100.0 * (
                (start_row + block_size) / (double)(num_rows * num_files) +
                i_file / (double)num_files));
        if (h->log && percent_next && *percent_done >= *percent_next)
        {
            oskar_log_message(h->log, 'S', -2, "%3d%% ...", *percent_done);
            *percent_next = 10 + 10 * (*percent_done / 10);
        }
    }
    oskar_mem_free(uvw, status);
    oskar_mem_free(u, status);
    oskar_mem_free(v, status);
    oskar_mem_free(w, status);
    oskar_mem_free(weight, status);
    oskar_mem_free(time_centroid, status);
    oskar_ms_close(ms);
#else
    (void) filename;
    (void) i_file;
    (void) num_files;
    (void) percent_done;
    (void) percent_next;
    oskar_log_error(h->log, "OSKAR was compiled without Measurement Set support.");
    *status = OSKAR_ERR_FUNCTION_NOT_AVAILABLE;
#endif
}
Ejemplo n.º 20
0
TEST(Log, oskar_log_error)
{
    oskar_Log* log = 0;
    oskar_log_error(log, "This is an error.");
    if (log) oskar_log_free(log);
}
void oskar_settings_load_observation(oskar_SettingsObservation* obs,
        oskar_Log* log, const char* filename, int* status)
{
    QByteArray t;
    QSettings s(QString(filename), QSettings::IniFormat);

    if (*status) return;

    s.beginGroup("observation");
    {
        // Get observation start time as MJD(UTC).
        QString str_st = s.value("start_time_utc").toString();
        if (str_st.size() > 0 && !str_st.contains(":"))
        {
            bool ok = false;
            obs->start_mjd_utc = str_st.toDouble(&ok);
            if (!ok)
            {
                oskar_log_error(log, "Invalid string for 'start_time_utc'.");
                *status = OSKAR_ERR_INVALID_ARGUMENT;
                return;
            }
        }
        else
        {
            const char* format_strings[] = {
                    "d-M-yyyy h:m:s.z", // British style
                    "d-M-yyyy h:m:s",
                    "yyyy/M/d/h:m:s.z", // CASA style
                    "yyyy/M/d/h:m:s",
                    "yyyy-MM-dd h:m:s.z", // International style
                    "yyyy-MM-dd h:m:s",
                    "yyyy-MM-ddTh:m:s.z", // ISO date style
                    "yyyy-MM-ddTh:m:s",
            };
            QDateTime st;
            int i = 0, num_formats = sizeof(format_strings) / sizeof(char*);
            for (i = 0; i < num_formats; ++i)
            {
                st = QDateTime::fromString(str_st, format_strings[i]);
                if (st.isValid())
                    break;
            }
            if (i == num_formats)
            {
                oskar_log_error(log, "Invalid string for 'start_time_utc'.");
                *status = OSKAR_ERR_INVALID_ARGUMENT;
                return;
            }
            int year   = st.date().year();
            int month  = st.date().month();
            int day    = st.date().day();
            int hour   = st.time().hour();
            int minute = st.time().minute();
            double second = st.time().second() + st.time().msec() / 1000.0;

            // Compute start time as MJD(UTC).
            double day_fraction = (hour + (minute / 60.0) +
                    (second / 3600.0)) / 24.0;
            obs->start_mjd_utc = convert_date_time_to_mjd(year,
                    month, day, day_fraction);
        }

        // Get number of time steps.
        obs->num_time_steps  = s.value("num_time_steps", 1).toInt();

        // Get observation length, either as number of seconds, or as a string.
        QString length = s.value("length").toString();
        if (length.size() > 0 && !length.contains(":"))
        {
            bool ok = false;
            obs->length_sec = length.toDouble(&ok);
            if (!ok)
            {
                oskar_log_error(log, "Invalid time string for 'length'");
                *status = OSKAR_ERR_INVALID_ARGUMENT;
                return;
            }
        }
        else
        {
            QTime len = QTime::fromString(length, "h:m:s.z");
            if (!len.isValid())
            {
                len = QTime::fromString(length, "h:m:s");
                if (!len.isValid())
                {
                    oskar_log_error(log, "Invalid time string for 'length' "
                            "(format must be hh:mm:ss.z).");
                    *status = OSKAR_ERR_INVALID_ARGUMENT;
                    return;
                }
            }
            obs->length_sec = len.hour() * 3600.0 +
                    len.minute() * 60.0 + len.second() + len.msec() / 1000.0;
        }
        obs->length_days = obs->length_sec / 86400.0;
    }
    s.endGroup();

    // Range checks.
    if (obs->num_time_steps <= 0) obs->num_time_steps = 1;

    // Compute interval
    obs->dt_dump_days = obs->length_days / obs->num_time_steps;
}
int main(int argc, char** argv)
{
    int status = 0;
    oskar::OptionParser opt("oskar_evaulate_pierce_points",
            oskar_version_string());
    opt.add_required("settings file");
    if (!opt.check_options(argc, argv)) return EXIT_FAILURE;

    const char* settings_file = opt.get_arg();

    // Create the log.
    oskar_Log* log = oskar_log_create(OSKAR_LOG_MESSAGE, OSKAR_LOG_STATUS);
    oskar_log_message(log, 'M', 0, "Running binary %s", argv[0]);

    // Enum values used in writing time-freq data binary files
    enum OSKAR_TIME_FREQ_TAGS
    {
        TIME_IDX       = 0,
        FREQ_IDX       = 1,
        TIME_MJD_UTC   = 2,
        FREQ_HZ        = 3,
        NUM_FIELDS     = 4,
        NUM_FIELD_TAGS = 5,
        HEADER_OFFSET  = 10,
        DATA           = 0,
        DIMS           = 1,
        LABEL          = 2,
        UNITS          = 3,
        GRP            = OSKAR_TAG_GROUP_TIME_FREQ_DATA
    };

    oskar_Settings_old settings;
    oskar_settings_old_load(&settings, log, settings_file, &status);
    oskar_log_set_keep_file(log, settings.sim.keep_log_file);
    if (status) return status;

    oskar_Telescope* tel = oskar_settings_to_telescope(&settings, log, &status);
    oskar_Sky* sky = oskar_settings_to_sky(&settings, log, &status);

    // FIXME remove this restriction ... (see evaluate Z)
    if (settings.ionosphere.num_TID_screens != 1)
        return OSKAR_ERR_SETUP_FAIL;

    int type = settings.sim.double_precision ? OSKAR_DOUBLE : OSKAR_SINGLE;
    int loc = OSKAR_CPU;

    int num_sources = oskar_sky_num_sources(sky);
    oskar_Mem *hor_x, *hor_y, *hor_z;
    hor_x = oskar_mem_create(type, loc, num_sources, &status);
    hor_y = oskar_mem_create(type, loc, num_sources, &status);
    hor_z = oskar_mem_create(type, loc, num_sources, &status);

    oskar_Mem *pp_lon, *pp_lat, *pp_rel_path;
    int num_stations = oskar_telescope_num_stations(tel);

    int num_pp = num_stations * num_sources;
    pp_lon = oskar_mem_create(type, loc, num_pp, &status);
    pp_lat = oskar_mem_create(type, loc, num_pp, &status);
    pp_rel_path = oskar_mem_create(type, loc, num_pp, &status);

    // Pierce points for one station (non-owned oskar_Mem pointers)
    oskar_Mem *pp_st_lon, *pp_st_lat, *pp_st_rel_path;
    pp_st_lon = oskar_mem_create_alias(0, 0, 0, &status);
    pp_st_lat = oskar_mem_create_alias(0, 0, 0, &status);
    pp_st_rel_path = oskar_mem_create_alias(0, 0, 0, &status);

    int num_times = settings.obs.num_time_steps;
    double obs_start_mjd_utc = settings.obs.start_mjd_utc;
    double dt_dump = settings.obs.dt_dump_days;

    // Binary file meta-data
    std::string label1 = "pp_lon";
    std::string label2 = "pp_lat";
    std::string label3 = "pp_path";
    std::string units  = "radians";
    std::string units2 = "";
    oskar_Mem *dims = oskar_mem_create(OSKAR_INT, loc, 2, &status);
    /* FIXME is this the correct dimension order ?
     * FIXME get the MATLAB reader to respect dimension ordering */
    oskar_mem_int(dims, &status)[0] = num_sources;
    oskar_mem_int(dims, &status)[1] = num_stations;

    const char* filename = settings.ionosphere.pierce_points.filename;
    oskar_Binary* h = oskar_binary_create(filename, 'w', &status);

    double screen_height_m = settings.ionosphere.TID->height_km * 1000.0;

//    printf("Number of times    = %i\n", num_times);
//    printf("Number of stations = %i\n", num_stations);

    void *x_, *y_, *z_;
    x_ = oskar_mem_void(oskar_telescope_station_true_x_offset_ecef_metres(tel));
    y_ = oskar_mem_void(oskar_telescope_station_true_y_offset_ecef_metres(tel));
    z_ = oskar_mem_void(oskar_telescope_station_true_z_offset_ecef_metres(tel));

    for (int t = 0; t < num_times; ++t)
    {
        double t_dump = obs_start_mjd_utc + t * dt_dump; // MJD UTC
        double gast = oskar_convert_mjd_to_gast_fast(t_dump + dt_dump / 2.0);

        for (int i = 0; i < num_stations; ++i)
        {
            const oskar_Station* station =
                    oskar_telescope_station_const(tel, i);
            double lon = oskar_station_lon_rad(station);
            double lat = oskar_station_lat_rad(station);
            double alt = oskar_station_alt_metres(station);
            double x_ecef, y_ecef, z_ecef, x_offset, y_offset, z_offset;

            if (type == OSKAR_DOUBLE)
            {
                x_offset = ((double*)x_)[i];
                y_offset = ((double*)y_)[i];
                z_offset = ((double*)z_)[i];
            }
            else
            {
                x_offset = (double)((float*)x_)[i];
                y_offset = (double)((float*)y_)[i];
                z_offset = (double)((float*)z_)[i];
            }

            oskar_convert_offset_ecef_to_ecef(1, &x_offset, &y_offset,
                    &z_offset, lon, lat, alt, &x_ecef, &y_ecef, &z_ecef);
            double last = gast + lon;

            if (type == OSKAR_DOUBLE)
            {
                oskar_convert_apparent_ra_dec_to_enu_directions_d(num_sources,
                        oskar_mem_double_const(oskar_sky_ra_rad_const(sky), &status),
                        oskar_mem_double_const(oskar_sky_dec_rad_const(sky), &status),
                        last, lat, oskar_mem_double(hor_x, &status),
                        oskar_mem_double(hor_y, &status),
                        oskar_mem_double(hor_z, &status));
            }
            else
            {
                oskar_convert_apparent_ra_dec_to_enu_directions_f(num_sources,
                        oskar_mem_float_const(oskar_sky_ra_rad_const(sky), &status),
                        oskar_mem_float_const(oskar_sky_dec_rad_const(sky), &status),
                        last, lat, oskar_mem_float(hor_x, &status),
                        oskar_mem_float(hor_y, &status),
                        oskar_mem_float(hor_z, &status));
            }

            int offset = i * num_sources;
            oskar_mem_set_alias(pp_st_lon, pp_lon, offset, num_sources,
                    &status);
            oskar_mem_set_alias(pp_st_lat, pp_lat, offset, num_sources,
                    &status);
            oskar_mem_set_alias(pp_st_rel_path, pp_rel_path, offset,
                    num_sources, &status);
            oskar_evaluate_pierce_points(pp_st_lon, pp_st_lat, pp_st_rel_path,
                    x_ecef, y_ecef, z_ecef, screen_height_m,
                    num_sources, hor_x, hor_y, hor_z, &status);
        } // Loop over stations.

        if (status != 0)
            continue;

        int index = t; // could be = (num_times * f) + t if we have frequency data
        int num_fields = 3;
        int num_field_tags = 4;
        double freq_hz = 0.0;
        int freq_idx = 0;

        // Write the header TAGS
        oskar_binary_write_int(h, GRP, TIME_IDX, index, t, &status);
        oskar_binary_write_double(h, GRP, FREQ_IDX, index, freq_idx, &status);
        oskar_binary_write_double(h, GRP, TIME_MJD_UTC, index, t_dump, &status);
        oskar_binary_write_double(h, GRP, FREQ_HZ, index, freq_hz, &status);
        oskar_binary_write_int(h, GRP, NUM_FIELDS, index, num_fields, &status);
        oskar_binary_write_int(h, GRP, NUM_FIELD_TAGS, index, num_field_tags,
                &status);

        // Write data TAGS (fields)
        int field, tagID;
        field = 0;
        tagID = HEADER_OFFSET + (num_field_tags * field);
        oskar_binary_write_mem(h, pp_lon, GRP, tagID + DATA,
                index, 0, &status);
        oskar_binary_write_mem(h, dims, GRP, tagID  + DIMS,
                index, 0, &status);
        oskar_binary_write(h, OSKAR_CHAR, GRP, tagID + LABEL,
                index, label1.size()+1, label1.c_str(), &status);
        oskar_binary_write(h, OSKAR_CHAR, GRP, tagID + UNITS,
                index, units.size()+1, units.c_str(), &status);
        field = 1;
        tagID = HEADER_OFFSET + (num_field_tags * field);
        oskar_binary_write_mem(h, pp_lat, GRP, tagID + DATA,
                index, 0, &status);
        oskar_binary_write_mem(h, dims, GRP, tagID  + DIMS,
                index, 0, &status);
        oskar_binary_write(h, OSKAR_CHAR, GRP, tagID + LABEL,
                index, label2.size()+1, label2.c_str(), &status);
        oskar_binary_write(h, OSKAR_CHAR, GRP, tagID + UNITS,
                index, units.size()+1, units.c_str(), &status);
        field = 2;
        tagID = HEADER_OFFSET + (num_field_tags * field);
        oskar_binary_write_mem(h, pp_rel_path, GRP, tagID + DATA,
                index, 0, &status);
        oskar_binary_write_mem(h, dims, GRP, tagID  + DIMS,
                index, 0, &status);
        oskar_binary_write(h, OSKAR_CHAR, GRP, tagID + LABEL,
                index, label3.size()+1, label3.c_str(), &status);
        oskar_binary_write(h, OSKAR_CHAR, GRP, tagID + UNITS,
                index, units2.size()+1, units2.c_str(), &status);
    } // Loop over times

    // Close the OSKAR binary file.
    oskar_binary_free(h);

    // clean up memory
    oskar_mem_free(hor_x, &status);
    oskar_mem_free(hor_y, &status);
    oskar_mem_free(hor_z, &status);
    oskar_mem_free(pp_lon, &status);
    oskar_mem_free(pp_lat, &status);
    oskar_mem_free(pp_rel_path, &status);
    oskar_mem_free(pp_st_lon, &status);
    oskar_mem_free(pp_st_lat, &status);
    oskar_mem_free(pp_st_rel_path, &status);
    oskar_mem_free(dims, &status);
    oskar_telescope_free(tel, &status);
    oskar_sky_free(sky, &status);

    // Check for errors.
    if (status)
        oskar_log_error(log, "Run failed: %s.", oskar_get_error_string(status));
    oskar_log_free(log);

    return status;
}