Exemple #1
0
TEST(binary_file, binary_read_write_mem)
{
    const char filename[] = "temp_test_mem_binary.dat";
    int num_cpu = 1000;
    int num_gpu = 2048;
    int status = 0;

    // Create the handle.
    oskar_Binary* h = oskar_binary_create(filename, 'w', &status);

    // Save data from CPU.
    {
        oskar_Mem* mem = oskar_mem_create(OSKAR_SINGLE, OSKAR_CPU,
                num_cpu, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        float* data = oskar_mem_float(mem, &status);

        // Fill array with data.
        for (int i = 0; i < num_cpu; ++i)
        {
            data[i] = i * 1024.0;
        }

        // Save CPU data.
        oskar_binary_write_mem_ext(h, mem, "USER", "TEST", 987654, 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_mem_free(mem, &status);
    }

    // Save data from GPU.
    {
        oskar_Mem *mem_cpu, *mem_gpu;
        mem_cpu = oskar_mem_create(OSKAR_DOUBLE_COMPLEX, OSKAR_CPU,
                num_gpu, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        double2* data = oskar_mem_double2(mem_cpu, &status);

        // Fill array with data.
        for (int i = 0; i < num_gpu; ++i)
        {
            data[i].x = i * 10.0;
            data[i].y = i * 20.0 + 1.0;
        }

        // Copy data to GPU.
        mem_gpu = oskar_mem_create_copy(mem_cpu, OSKAR_GPU, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);

        // Save GPU data.
        oskar_binary_write_mem_ext(h, mem_gpu, "AA", "BB", 2, 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_mem_free(mem_cpu, &status);
        oskar_mem_free(mem_gpu, &status);
    }

    // Save a single integer with a large index.
    int val = 0xFFFFFF;
    oskar_binary_write_int(h, 50, 9, 800000, val, &status);
    ASSERT_EQ(0, status) << oskar_get_error_string(status);

    // Save data from CPU with blank tags.
    {
        oskar_Mem* mem = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU,
                num_cpu, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        double* data = oskar_mem_double(mem, &status);

        // Fill array with data.
        for (int i = 0; i < num_cpu; ++i)
        {
            data[i] = i * 500.0;
        }

        // Save CPU data.
        oskar_binary_write_mem_ext(h, mem, "", "", 10, 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);

        // Fill array with data.
        for (int i = 0; i < num_cpu; ++i)
        {
            data[i] = i * 501.0;
        }

        // Save CPU data.
        oskar_binary_write_mem_ext(h, mem, "", "", 11, 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_mem_free(mem, &status);
    }

    // Save CPU data with tags that are equal lengths.
    {
        oskar_Mem* mem = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU,
                num_cpu, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        double* data = oskar_mem_double(mem, &status);

        // Fill array with data.
        for (int i = 0; i < num_cpu; ++i)
        {
            data[i] = i * 1001.0;
        }

        // Save CPU data.
        oskar_binary_write_mem_ext(h, mem, "DOG", "CAT", 0, 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);

        // Fill array with data.
        for (int i = 0; i < num_cpu; ++i)
        {
            data[i] = i * 127.0;
        }

        // Save CPU data.
        oskar_binary_write_mem_ext(h, mem, "ONE", "TWO", 0, 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_mem_free(mem, &status);
    }

    // Create the handle for reading.
    oskar_binary_free(h);
    h = oskar_binary_create(filename, 'r', &status);

    // Load data directly to GPU.
    {
        oskar_Mem *mem_gpu, *mem_cpu;
        mem_gpu = oskar_mem_create(OSKAR_DOUBLE_COMPLEX, OSKAR_GPU,
                0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_binary_read_mem_ext(h, mem_gpu, "AA", "BB", 2, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        EXPECT_EQ(num_gpu, (int)oskar_mem_length(mem_gpu));

        // Copy back to CPU and examine contents.
        mem_cpu = oskar_mem_create_copy(mem_gpu, OSKAR_CPU, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        double2* data = oskar_mem_double2(mem_cpu, &status);
        for (int i = 0; i < num_gpu; ++i)
        {
            EXPECT_DOUBLE_EQ(i * 10.0,       data[i].x);
            EXPECT_DOUBLE_EQ(i * 20.0 + 1.0, data[i].y);
        }
        oskar_mem_free(mem_cpu, &status);
        oskar_mem_free(mem_gpu, &status);
    }

    // Load integer with a large index.
    int new_val = 0;
    oskar_binary_read_int(h, 50, 9, 800000, &new_val, &status);
    ASSERT_EQ(0, status) << oskar_get_error_string(status);
    EXPECT_EQ(val, new_val);

    // Load CPU data.
    {
        oskar_Mem* mem = oskar_mem_create(OSKAR_SINGLE, OSKAR_CPU,
                num_cpu, &status);
        oskar_binary_read_mem_ext(h, mem, "USER", "TEST", 987654, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        ASSERT_EQ(num_cpu, (int)oskar_mem_length(mem));
        float* data = oskar_mem_float(mem, &status);
        for (int i = 0; i < num_cpu; ++i)
        {
            EXPECT_DOUBLE_EQ(i * 1024.0, data[i]);
        }
        oskar_mem_free(mem, &status);
    }

    // Load CPU data with blank tags.
    {
        double* data;
        oskar_Mem* mem = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU,
                num_cpu, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_binary_read_mem_ext(h, mem, "", "", 10, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_binary_read_mem_ext(h, mem, "DOESN'T", "EXIST", 10, &status);
        EXPECT_EQ((int)OSKAR_ERR_BINARY_TAG_NOT_FOUND, status);
        status = 0;
        ASSERT_EQ(num_cpu, (int)oskar_mem_length(mem));
        data = oskar_mem_double(mem, &status);
        for (int i = 0; i < num_cpu; ++i)
        {
            EXPECT_DOUBLE_EQ(i * 500.0, data[i]);
        }
        oskar_binary_read_mem_ext(h, mem, "", "", 11, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        ASSERT_EQ(num_cpu, (int)oskar_mem_length(mem));
        data = oskar_mem_double(mem, &status);
        for (int i = 0; i < num_cpu; ++i)
        {
            EXPECT_DOUBLE_EQ(i * 501.0, data[i]);
        }
        oskar_mem_free(mem, &status);
    }

    // Load CPU data with tags that are equal lengths.
    {
        double* data;
        oskar_Mem* mem = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, 0,
                &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_binary_read_mem_ext(h, mem, "ONE", "TWO", 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        ASSERT_EQ(num_cpu, (int)oskar_mem_length(mem));
        data = oskar_mem_double(mem, &status);
        for (int i = 0; i < num_cpu; ++i)
        {
            EXPECT_DOUBLE_EQ(i * 127.0, data[i]);
        }
        oskar_binary_read_mem_ext(h, mem, "DOG", "CAT", 0, &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        ASSERT_EQ(num_cpu, (int)oskar_mem_length(mem));
        data = oskar_mem_double(mem, &status);
        for (int i = 0; i < num_cpu; ++i)
        {
            EXPECT_DOUBLE_EQ(i * 1001.0, data[i]);
        }
        oskar_mem_free(mem, &status);
    }

    // Try to load data that isn't present.
    {
        oskar_Mem* mem = oskar_mem_create(OSKAR_DOUBLE, OSKAR_CPU, 0,
                &status);
        ASSERT_EQ(0, status) << oskar_get_error_string(status);
        oskar_binary_read_mem_ext(h, mem, "DOESN'T", "EXIST", 10, &status);
        EXPECT_EQ((int)OSKAR_ERR_BINARY_TAG_NOT_FOUND, status);
        status = 0;
        EXPECT_EQ(0, (int)oskar_mem_length(mem));
        oskar_mem_free(mem, &status);
    }

    // Release the handle.
    oskar_binary_free(h);
    ASSERT_EQ(0, status) << oskar_get_error_string(status);
}
Exemple #2
0
oskar_Sky* oskar_sky_read(const char* filename, int location, int* status)
{
    int type = 0, num_sources = 0, idx = 0;
    oskar_Binary* h = 0;
    unsigned char group = OSKAR_TAG_GROUP_SKY_MODEL;
    oskar_Sky* sky = 0;

    /* Check if safe to proceed. */
    if (*status) return 0;

    /* Create the handle. */
    h = oskar_binary_create(filename, 'r', status);

    /* Read the sky model data parameters. */
    oskar_binary_read_int(h, group, OSKAR_SKY_TAG_NUM_SOURCES, idx,
            &num_sources, status);
    oskar_binary_read_int(h, group, OSKAR_SKY_TAG_DATA_TYPE, idx,
            &type, status);

    /* Check if safe to proceed.
     * Status flag will be set if binary read failed. */
    if (*status)
    {
        oskar_binary_free(h);
        return 0;
    }

    /* Create the sky model structure. */
    sky = oskar_sky_create(type, location, num_sources, status);

    /* Read the arrays. */
    oskar_binary_read_mem(h, oskar_sky_ra_rad(sky),
            group, OSKAR_SKY_TAG_RA, idx, status);
    oskar_binary_read_mem(h, oskar_sky_dec_rad(sky),
            group, OSKAR_SKY_TAG_DEC, idx, status);
    oskar_binary_read_mem(h, oskar_sky_I(sky),
            group, OSKAR_SKY_TAG_STOKES_I, idx, status);
    oskar_binary_read_mem(h, oskar_sky_Q(sky),
            group, OSKAR_SKY_TAG_STOKES_Q, idx, status);
    oskar_binary_read_mem(h, oskar_sky_U(sky),
            group, OSKAR_SKY_TAG_STOKES_U, idx, status);
    oskar_binary_read_mem(h, oskar_sky_V(sky),
            group, OSKAR_SKY_TAG_STOKES_V, idx, status);
    oskar_binary_read_mem(h, oskar_sky_reference_freq_hz(sky),
            group, OSKAR_SKY_TAG_REF_FREQ, idx, status);
    oskar_binary_read_mem(h, oskar_sky_spectral_index(sky),
            group, OSKAR_SKY_TAG_SPECTRAL_INDEX, idx, status);
    oskar_binary_read_mem(h, oskar_sky_fwhm_major_rad(sky),
            group, OSKAR_SKY_TAG_FWHM_MAJOR, idx, status);
    oskar_binary_read_mem(h, oskar_sky_fwhm_minor_rad(sky),
            group, OSKAR_SKY_TAG_FWHM_MINOR, idx, status);
    oskar_binary_read_mem(h, oskar_sky_position_angle_rad(sky),
            group, OSKAR_SKY_TAG_POSITION_ANGLE, idx, status);
    oskar_binary_read_mem(h, oskar_sky_rotation_measure_rad(sky),
            group, OSKAR_SKY_TAG_ROTATION_MEASURE, idx, status);

    /* Release the handle. */
    oskar_binary_free(h);

    /* Return a handle to the sky model, or NULL if an error occurred. */
    if (*status)
    {
        oskar_sky_free(sky, status);
        sky = 0;
    }
    return sky;
}
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;
}
int main(int argc, char** argv)
{
    // ===== Declare options ==================================================
    oskar::OptionParser opt("oskar_vis_to_ascii_table", oskar_version_string());
    opt.set_description("Converts an OSKAR visibility binary file to an ASCII "
            "table format with the following columns:\n "
            "[1] index, [2] baseline-uu, [3] baseline-vv, [4] baseline-ww "
            "[5] Real, [6] Imag. "
            "The table is written out in baseline-time order where baseline "
            "is the fastest varying dimension");
    opt.add_required("OSKAR vis file");
    opt.add_optional("output file name");
    opt.add_flag("-c", "Channel index to write to file. (default = 0)", 1, "0",
            false, "--channel");
    opt.add_flag("-p", "Polarisation ID to write to file. (default = 0) "
            "(0=I, 1=Q, 2=U, 3=V, 4=XX, 5=XY, 6=YX, 7=YY)",
            1, "0", false, "--polarisation");
    opt.add_flag("-t", "Time index to write to file. (default = all)", 1, "",
            false, "--time");
    opt.add_flag("-w", "Output baseline coordinates in wavelengths. "
            "(default = metres)", false, "--baseline_wavelengths");
    opt.add_flag("-h", "Write a summary header in the ASCII table. ");
    opt.add_flag("-v", "Verbose mode.");
    opt.add_flag("--csv", "Write in CSV format");
    opt.add_flag("-s", "Write output table to standard output instead of to file.",
            false, "--stdout");
    if (!opt.check_options(argc, argv)) return EXIT_FAILURE;

    // ===== Read options ====================================================
    const char* vis_file = opt.get_arg(0);
    std::string txt_file;
    if (opt.num_args() == 2)
        txt_file = std::string(opt.get_arg(1));
    else {
        txt_file = std::string(vis_file) + ".txt";
    }
    int c = 0, p = 0, t = -1;
    if (opt.is_set("-c")) c = opt.get_int("-c");
    if (opt.is_set("-p")) p = opt.get_int("-p");
    if (opt.is_set("-t")) t = opt.get_int("-t");
    bool metres = !opt.is_set("-w");
    bool write_header = opt.is_set("-h");
    bool csv = opt.is_set("--csv");
    bool verbose = opt.is_set("-v");

    // ===== Write table ======================================================
    int status = 0;
    oskar_Binary* h = oskar_binary_create(vis_file, 'r', &status);
    oskar_Vis* vis = oskar_vis_read(h, &status);
    if (status)
    {
        fprintf(stderr, "ERROR: Unable to read specified visibility file: %s\n",
                vis_file);
        oskar_vis_free(vis, &status);
        oskar_binary_free(h);
        return status;
    }
    oskar_binary_free(h);

    int num_chan = oskar_vis_num_channels(vis);
    int num_times = oskar_vis_num_times(vis);
    int num_baselines = oskar_vis_num_baselines(vis);
    int num_pol = oskar_vis_num_pols(vis);
    int num_stations = oskar_vis_num_stations(vis);
    int total_vis = num_chan * num_times * num_baselines * num_pol;
    double freq_start_hz = oskar_vis_freq_start_hz(vis);
    double freq_inc_hz = oskar_vis_freq_inc_hz(vis);
    double freq_hz = freq_start_hz + c * freq_inc_hz;
    double lambda_m = 299792458.0 / freq_hz;

    if (t != -1 && t > num_times-1) {
        fprintf(stderr, "ERROR: Time index out of range.\n");
        return EXIT_FAILURE;
    }
    if (c > num_chan-1) {
        fprintf(stderr, "ERROR: Channel index out of range.\n");
        return EXIT_FAILURE;
    }


    FILE* out;
    if (!opt.is_set("-s")) {
        out = fopen(txt_file.c_str(), "w");
        if (out == NULL) return EXIT_FAILURE;
    }
    else {
        out = stdout;
    }

    const oskar_Mem* uu = oskar_vis_baseline_uu_metres_const(vis);
    const oskar_Mem* vv = oskar_vis_baseline_vv_metres_const(vis);
    const oskar_Mem* ww = oskar_vis_baseline_ww_metres_const(vis);
    const oskar_Mem* amp = oskar_vis_amplitude_const(vis);
    // amplitudes dims: channel x times x baselines x pol
    int amp_offset = c * num_times * num_baselines;
    if (t != -1) amp_offset += t * num_baselines;
    // baseline dims: times x baselines
    int baseline_offset = 0;
    if (t != -1) baseline_offset = t * num_baselines;
    int type = oskar_mem_type(uu);

    int num_vis_out = num_baselines;
    if (t == -1) num_vis_out *= num_times;

    if (verbose) {
        write_header_(stdout, total_vis, num_chan, num_times, num_baselines,
                num_pol, num_stations, num_vis_out, c, freq_hz, lambda_m, p, t,
                metres);
#if 0
        fprintf(stdout, "amp_offset      = %i\n", amp_offset);
        fprintf(stdout, "baseline_offset = %i\n", baseline_offset);
#endif
    }

    // Write header if specified
    if (write_header)
    {
        write_header_(out, total_vis, num_chan, num_times, num_baselines,
                num_pol, num_stations, num_vis_out, c, freq_hz, lambda_m, p, t,
                metres);
        char pre = '#';
        fprintf(out, "%c\n", pre);
        fprintf(out, "%c %s %-14s %-15s %-15s %-23s %-15s\n",
                pre, "Idx", " uu", "  vv", "  ww", "  Amp. Re.", "  Amp. Im.");
    }

    if (type == OSKAR_DOUBLE)
    {
        const double* uu_ = oskar_mem_double_const(uu, &status);
        const double* vv_ = oskar_mem_double_const(vv, &status);
        const double* ww_ = oskar_mem_double_const(ww, &status);
        const double4c* amp_ = oskar_mem_double4c_const(amp, &status);
        int aIdx = amp_offset;
        int bIdx = baseline_offset;
        for (int i = 0; i < num_vis_out; ++i, ++bIdx, ++aIdx)
        {
            double2 a = getPolAmp_<double2, double4c>(amp_[aIdx], p);
            double buu = (metres)? uu_[bIdx] : uu_[bIdx]/lambda_m;
            double bvv = (metres)? vv_[bIdx] : vv_[bIdx]/lambda_m;
            double bww = (metres)? ww_[bIdx] : ww_[bIdx]/lambda_m;
            writeData_<double, double2>(i, buu, bvv, bww, a, csv, out);
        }
    }
    else // OSKAR_SINGLE
    {
        const float* uu_ = oskar_mem_float_const(uu, &status);
        const float* vv_ = oskar_mem_float_const(vv, &status);
        const float* ww_ = oskar_mem_float_const(ww, &status);
        const float4c* amp_ = oskar_mem_float4c_const(amp, &status);
        int aIdx = amp_offset;
        int bIdx = baseline_offset;
        for (int i = 0; i < num_vis_out; ++i, ++bIdx, ++aIdx)
        {
            float2 a = getPolAmp_<float2, float4c>(amp_[aIdx], p);
            float buu = (metres)? uu_[bIdx] : uu_[bIdx]/lambda_m;
            float bvv = (metres)? vv_[bIdx] : vv_[bIdx]/lambda_m;
            float bww = (metres)? ww_[bIdx] : ww_[bIdx]/lambda_m;
            writeData_<float, float2>(i, buu, bvv, bww, a, csv, out);
        }
    }

    fclose(out);
    oskar_vis_free(vis, &status);

    return status;
}
void oskar_imager_read_coords_vis(oskar_Imager* h, const char* filename,
        int i_file, int num_files, int* percent_done, int* percent_next,
        int* status)
{
    oskar_Binary* vis_file;
    oskar_VisHeader* header;
    oskar_Mem *uu, *vv, *ww, *weight, *time_centroid, *time_slice;
    int coord_prec, max_times_per_block, tags_per_block, i_block, num_blocks;
    int num_times_total, num_stations, num_baselines, num_pols;
    double time_start_mjd, time_inc_sec;
    if (*status) return;

    /* Read the header. */
    vis_file = oskar_binary_create(filename, 'r', status);
    header = oskar_vis_header_read(vis_file, status);
    if (*status)
    {
        oskar_vis_header_free(header, status);
        oskar_binary_free(vis_file);
        return;
    }
    coord_prec = oskar_vis_header_coord_precision(header);
    max_times_per_block = oskar_vis_header_max_times_per_block(header);
    tags_per_block = oskar_vis_header_num_tags_per_block(header);
    num_times_total = oskar_vis_header_num_times_total(header);
    num_stations = oskar_vis_header_num_stations(header);
    num_baselines = num_stations * (num_stations - 1) / 2;
    num_pols = oskar_type_is_matrix(oskar_vis_header_amp_type(header)) ? 4 : 1;
    num_blocks = (num_times_total + max_times_per_block - 1) /
            max_times_per_block;
    time_start_mjd = oskar_vis_header_time_start_mjd_utc(header) * 86400.0;
    time_inc_sec = oskar_vis_header_time_inc_sec(header);

    /* Set visibility meta-data. */
    oskar_imager_set_vis_frequency(h,
            oskar_vis_header_freq_start_hz(header),
            oskar_vis_header_freq_inc_hz(header),
            oskar_vis_header_num_channels_total(header));
    oskar_imager_set_vis_phase_centre(h,
            oskar_vis_header_phase_centre_ra_deg(header),
            oskar_vis_header_phase_centre_dec_deg(header));

    /* Create scratch arrays. Weights are all 1. */
    uu = oskar_mem_create(coord_prec, OSKAR_CPU, 0, status);
    vv = oskar_mem_create(coord_prec, OSKAR_CPU, 0, status);
    ww = oskar_mem_create(coord_prec, OSKAR_CPU, 0, status);
    time_centroid = oskar_mem_create(OSKAR_DOUBLE,
            OSKAR_CPU, num_baselines * max_times_per_block, status);
    time_slice = oskar_mem_create_alias(0, 0, 0, status);
    weight = oskar_mem_create(h->imager_prec,
            OSKAR_CPU, num_baselines * num_pols * max_times_per_block, status);
    oskar_mem_set_value_real(weight, 1.0, 0, 0, status);

    /* Loop over visibility blocks. */
    for (i_block = 0; i_block < num_blocks; ++i_block)
    {
        int t, num_times, num_channels, start_time, start_chan, end_chan;
        int dim_start_and_size[6];
        size_t num_rows;
        if (*status) break;

        /* Read block metadata. */
        oskar_timer_resume(h->tmr_read);
        oskar_binary_set_query_search_start(vis_file,
                i_block * tags_per_block, status);
        oskar_binary_read(vis_file, OSKAR_INT,
                OSKAR_TAG_GROUP_VIS_BLOCK,
                OSKAR_VIS_BLOCK_TAG_DIM_START_AND_SIZE, i_block,
                sizeof(dim_start_and_size), dim_start_and_size, status);
        start_time   = dim_start_and_size[0];
        start_chan   = dim_start_and_size[1];
        num_times    = dim_start_and_size[2];
        num_channels = dim_start_and_size[3];
        num_rows     = num_times * num_baselines;
        end_chan     = start_chan + num_channels - 1;

        /* Fill in the time centroid values. */
        for (t = 0; t < num_times; ++t)
        {
            oskar_mem_set_alias(time_slice, time_centroid,
                    t * num_baselines, num_baselines, status);
            oskar_mem_set_value_real(time_slice,
                    time_start_mjd + (start_time + t + 0.5) * time_inc_sec,
                    0, num_baselines, status);
        }

        /* Read the baseline coordinates. */
        oskar_binary_read_mem(vis_file, uu, OSKAR_TAG_GROUP_VIS_BLOCK,
                OSKAR_VIS_BLOCK_TAG_BASELINE_UU, i_block, status);
        oskar_binary_read_mem(vis_file, vv, OSKAR_TAG_GROUP_VIS_BLOCK,
                OSKAR_VIS_BLOCK_TAG_BASELINE_VV, i_block, status);
        oskar_binary_read_mem(vis_file, ww, OSKAR_TAG_GROUP_VIS_BLOCK,
                OSKAR_VIS_BLOCK_TAG_BASELINE_WW, i_block, status);

        /* Update the imager with the data. */
        oskar_timer_pause(h->tmr_read);
        oskar_imager_update(h, num_rows, start_chan, end_chan, num_pols,
                uu, vv, ww, 0, weight, time_centroid, status);
        *percent_done = (int) round(100.0 * (
                (i_block + 1) / (double)(num_blocks * 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(uu, status);
    oskar_mem_free(vv, status);
    oskar_mem_free(ww, status);
    oskar_mem_free(weight, status);
    oskar_mem_free(time_centroid, status);
    oskar_mem_free(time_slice, status);
    oskar_vis_header_free(header, status);
    oskar_binary_free(vis_file);
}