void oskar_element_load_scalar(oskar_Element* data,
        double freq_hz, const char* filename,
        double closeness, double closeness_inc, int ignore_at_poles,
        int ignore_below_horizon, int* status)
{
    int i, n = 0, type = OSKAR_DOUBLE;
    oskar_Splines *scalar_re = 0, *scalar_im = 0;
    oskar_Mem *theta = 0, *phi = 0, *re = 0, *im = 0, *weight = 0;

    /* Declare the line buffer. */
    char *line = NULL;
    size_t bufsize = 0;
    FILE* file;

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

    /* Check the data type. */
    if (oskar_element_precision(data) != type)
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }

    /* Check the location. */
    if (oskar_element_mem_location(data) != OSKAR_CPU)
    {
        *status = OSKAR_ERR_BAD_LOCATION;
        return;
    }

    /* Check if this frequency has already been set, and get its index if so. */
    n = data->num_freq;
    for (i = 0; i < n; ++i)
    {
        if (fabs(data->freqs_hz[i] - freq_hz) <= freq_hz * DBL_EPSILON)
            break;
    }

    /* Expand arrays to hold data for a new frequency, if needed. */
    if (i >= data->num_freq)
    {
        i = data->num_freq;
        oskar_element_resize_freq_data(data, i + 1, status);
        data->freqs_hz[i] = freq_hz;
    }

    /* Get pointers to surface data based on frequency index. */
    scalar_re = data->scalar_re[i];
    scalar_im = data->scalar_im[i];

    /* Open the file. */
    file = fopen(filename, "r");
    if (!file)
    {
        *status = OSKAR_ERR_FILE_IO;
        return;
    }

    /* Create local arrays to hold data for fitting. */
    theta  = oskar_mem_create(type, OSKAR_CPU, 0, status);
    phi    = oskar_mem_create(type, OSKAR_CPU, 0, status);
    re     = oskar_mem_create(type, OSKAR_CPU, 0, status);
    im     = oskar_mem_create(type, OSKAR_CPU, 0, status);
    weight = oskar_mem_create(type, OSKAR_CPU, 0, status);
    if (*status) return;

    /* Loop over and read each line in the file. */
    n = 0;
    while (oskar_getline(&line, &bufsize, file) != OSKAR_ERR_EOF)
    {
        double re_, im_;
        double par[] = {0., 0., 0., 0.}; /* theta, phi, amp, phase (optional) */
        void *p_theta = 0, *p_phi = 0, *p_re = 0, *p_im = 0, *p_weight = 0;

        /* Parse the line, and skip if data were not read correctly. */
        if (oskar_string_to_array_d(line, 4, par) < 3)
            continue;

        /* Ignore data below horizon if requested. */
        if (ignore_below_horizon && par[0] > 90.0) continue;

        /* Ignore data at poles if requested. */
        if (ignore_at_poles)
            if (par[0] < 1e-6 || par[0] > (180.0 - 1e-6)) continue;

        /* Convert angular measures to radians. */
        par[0] *= DEG2RAD; /* theta */
        par[1] *= DEG2RAD; /* phi */
        par[3] *= DEG2RAD; /* phase */

        /* Ensure enough space in arrays. */
        if (n % 100 == 0)
        {
            const int size = n + 100;
            oskar_mem_realloc(theta, size, status);
            oskar_mem_realloc(phi, size, status);
            oskar_mem_realloc(re, size, status);
            oskar_mem_realloc(im, size, status);
            oskar_mem_realloc(weight, size, status);
            if (*status) break;
        }
        p_theta  = oskar_mem_void(theta);
        p_phi    = oskar_mem_void(phi);
        p_re     = oskar_mem_void(re);
        p_im     = oskar_mem_void(im);
        p_weight = oskar_mem_void(weight);

        /* Amp,phase to real,imag conversion. */
        re_ = par[2] * cos(par[3]);
        im_ = par[2] * sin(par[3]);

        /* Store the surface data. */
        ((double*)p_theta)[n]  = par[0];
        ((double*)p_phi)[n]    = par[1];
        ((double*)p_re)[n]     = re_;
        ((double*)p_im)[n]     = im_;
        ((double*)p_weight)[n] = 1.0;

        /* Increment array pointer. */
        n++;
    }

    /* Free the line buffer and close the file. */
    free(line);
    fclose(file);

    /* Fit splines to the surface data. */
    fit_splines(scalar_re, n, theta, phi, re, weight,
            closeness, closeness_inc, "Scalar [real]", status);
    fit_splines(scalar_im, n, theta, phi, im, weight,
            closeness, closeness_inc, "Scalar [imag]", status);

    /* Store the filename. */
    oskar_mem_append_raw(data->filename_scalar[i], filename, OSKAR_CHAR,
                OSKAR_CPU, 1 + strlen(filename), status);

    /* Free local arrays. */
    oskar_mem_free(theta, status);
    oskar_mem_free(phi, status);
    oskar_mem_free(re, status);
    oskar_mem_free(im, status);
    oskar_mem_free(weight, status);
}
void oskar_element_load_cst(oskar_Element* data, oskar_Log* log,
        int port, double freq_hz, const char* filename,
        double closeness, double closeness_inc, int ignore_at_poles,
        int ignore_below_horizon, int* status)
{
    int i, n = 0, type = OSKAR_DOUBLE;
    size_t fname_len;
    oskar_Splines *data_h_re = 0, *data_h_im = 0;
    oskar_Splines *data_v_re = 0, *data_v_im = 0;
    oskar_Mem *theta, *phi, *h_re, *h_im, *v_re, *v_im, *weight;

    /* Declare the line buffer. */
    char *line = 0, *dbi = 0, *ludwig3 = 0;
    size_t bufsize = 0;
    FILE* file;

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

    /* Check port number. */
    if (port != 0 && port != 1 && port != 2)
    {
        *status = OSKAR_ERR_INVALID_ARGUMENT;
        return;
    }

    /* Check the data type. */
    if (oskar_element_precision(data) != type)
    {
        *status = OSKAR_ERR_TYPE_MISMATCH;
        return;
    }

    /* Check the location. */
    if (oskar_element_mem_location(data) != OSKAR_CPU)
    {
        *status = OSKAR_ERR_BAD_LOCATION;
        return;
    }

    /* Check if this frequency has already been set, and get its index if so. */
    n = data->num_freq;
    for (i = 0; i < n; ++i)
    {
        if (fabs(data->freqs_hz[i] - freq_hz) <= freq_hz * DBL_EPSILON)
            break;
    }

    /* Expand arrays to hold data for a new frequency, if needed. */
    if (i >= data->num_freq)
    {
        i = data->num_freq;
        oskar_element_resize_freq_data(data, i + 1, status);
        data->freqs_hz[i] = freq_hz;
    }

    /* Get pointers to surface data based on port number and frequency index. */
    if (port == 1 || port == 0)
    {
        data_h_re = oskar_element_x_h_re(data, i);
        data_h_im = oskar_element_x_h_im(data, i);
        data_v_re = oskar_element_x_v_re(data, i);
        data_v_im = oskar_element_x_v_im(data, i);
    }
    else if (port == 2)
    {
        data_h_re = oskar_element_y_h_re(data, i);
        data_h_im = oskar_element_y_h_im(data, i);
        data_v_re = oskar_element_y_v_re(data, i);
        data_v_im = oskar_element_y_v_im(data, i);
    }

    /* Open the file. */
    fname_len = 1 + strlen(filename);
    file = fopen(filename, "r");
    if (!file)
    {
        *status = OSKAR_ERR_FILE_IO;
        return;
    }

    /* Read the first line to check units and coordinate system. */
    if (oskar_getline(&line, &bufsize, file) < 0)
    {
        *status = OSKAR_ERR_FILE_IO;
        free(line);
        fclose(file);
        return;
    }

    /* Check for presence of "dBi". */
    dbi = strstr(line, "dBi");

    /* Check for data in Ludwig-3 polarisation system. */
    ludwig3 = strstr(line, "Horiz");

    /* Create local arrays to hold data for fitting. */
    theta  = oskar_mem_create(type, OSKAR_CPU, 0, status);
    phi    = oskar_mem_create(type, OSKAR_CPU, 0, status);
    h_re   = oskar_mem_create(type, OSKAR_CPU, 0, status);
    h_im   = oskar_mem_create(type, OSKAR_CPU, 0, status);
    v_re   = oskar_mem_create(type, OSKAR_CPU, 0, status);
    v_im   = oskar_mem_create(type, OSKAR_CPU, 0, status);
    weight = oskar_mem_create(type, OSKAR_CPU, 0, status);
    if (*status) return;

    /* Loop over and read each line in the file. */
    n = 0;
    while (oskar_getline(&line, &bufsize, file) != OSKAR_ERR_EOF)
    {
        double t = 0., p = 0., abs_theta_horiz, phase_theta_horiz;
        double abs_phi_verti, phase_phi_verti;
        double theta_horiz_re, theta_horiz_im, phi_verti_re, phi_verti_im;
        double h_re_, h_im_, v_re_, v_im_;
        void *p_theta = 0, *p_phi = 0, *p_h_re = 0, *p_h_im = 0, *p_v_re = 0;
        void *p_v_im = 0, *p_weight = 0;

        /* Parse the line, and skip if data were not read correctly. */
        if (sscanf(line, "%lf %lf %*f %lf %lf %lf %lf %*f", &t, &p,
                    &abs_theta_horiz, &phase_theta_horiz,
                    &abs_phi_verti, &phase_phi_verti) != 6)
            continue;

        /* Ignore data below horizon if requested. */
        if (ignore_below_horizon && t > 90.0) continue;

        /* Ignore data at poles if requested. */
        if (ignore_at_poles)
            if (t < 1e-6 || t > (180.0 - 1e-6)) continue;

        /* Convert angular measures to radians. */
        t *= DEG2RAD;
        p *= DEG2RAD;
        phase_theta_horiz *= DEG2RAD;
        phase_phi_verti *= DEG2RAD;

        /* Ensure enough space in arrays. */
        if (n % 100 == 0)
        {
            int size;
            size = n + 100;
            oskar_mem_realloc(theta, size, status);
            oskar_mem_realloc(phi, size, status);
            oskar_mem_realloc(h_re, size, status);
            oskar_mem_realloc(h_im, size, status);
            oskar_mem_realloc(v_re, size, status);
            oskar_mem_realloc(v_im, size, status);
            oskar_mem_realloc(weight, size, status);
            if (*status) break;
        }
        p_theta  = oskar_mem_void(theta);
        p_phi    = oskar_mem_void(phi);
        p_h_re   = oskar_mem_void(h_re);
        p_h_im   = oskar_mem_void(h_im);
        p_v_re   = oskar_mem_void(v_re);
        p_v_im   = oskar_mem_void(v_im);
        p_weight = oskar_mem_void(weight);

        /* Convert decibel to linear scale if necessary. */
        if (dbi)
        {
            abs_theta_horiz = pow(10.0, abs_theta_horiz / 10.0);
            abs_phi_verti   = pow(10.0, abs_phi_verti / 10.0);
        }

        /* Amp,phase to real,imag conversion. */
        theta_horiz_re = abs_theta_horiz * cos(phase_theta_horiz);
        theta_horiz_im = abs_theta_horiz * sin(phase_theta_horiz);
        phi_verti_re = abs_phi_verti * cos(phase_phi_verti);
        phi_verti_im = abs_phi_verti * sin(phase_phi_verti);

        /* Convert to Ludwig-3 polarisation system if required. */
        if (ludwig3)
        {
            /* Already in Ludwig-3: No conversion required. */
            h_re_ = theta_horiz_re;
            h_im_ = theta_horiz_im;
            v_re_ = phi_verti_re;
            v_im_ = phi_verti_im;
        }
        else
        {
            /* Convert from theta/phi to Ludwig-3. */
            double cos_p, sin_p;
            sin_p = sin(p);
            cos_p = cos(p);
            h_re_ = theta_horiz_re * cos_p - phi_verti_re * sin_p;
            h_im_ = theta_horiz_im * cos_p - phi_verti_im * sin_p;
            v_re_ = theta_horiz_re * sin_p + phi_verti_re * cos_p;
            v_im_ = theta_horiz_im * sin_p + phi_verti_im * cos_p;
        }

        /* Store the surface data in Ludwig-3 format. */
        ((double*)p_theta)[n]  = t;
        ((double*)p_phi)[n]    = p;
        ((double*)p_h_re)[n]   = h_re_;
        ((double*)p_h_im)[n]   = h_im_;
        ((double*)p_v_re)[n]   = v_re_;
        ((double*)p_v_im)[n]   = v_im_;
        ((double*)p_weight)[n] = 1.0;

        /* Increment array pointer. */
        n++;
    }

    /* Free the line buffer and close the file. */
    free(line);
    fclose(file);

    /* Fit splines to the surface data. */
    fit_splines(log, data_h_re, n, theta, phi, h_re, weight,
            closeness, closeness_inc, "H [real]", status);
    fit_splines(log, data_h_im, n, theta, phi, h_im, weight,
            closeness, closeness_inc, "H [imag]", status);
    fit_splines(log, data_v_re, n, theta, phi, v_re, weight,
            closeness, closeness_inc, "V [real]", status);
    fit_splines(log, data_v_im, n, theta, phi, v_im, weight,
            closeness, closeness_inc, "V [imag]", status);

    /* Store the filename. */
    if (port == 0)
    {
        oskar_mem_append_raw(data->filename_x[i], filename, OSKAR_CHAR,
                OSKAR_CPU, fname_len, status);
        oskar_mem_append_raw(data->filename_y[i], filename, OSKAR_CHAR,
                OSKAR_CPU, fname_len, status);
    }
    else if (port == 1)
    {
        oskar_mem_append_raw(data->filename_x[i], filename, OSKAR_CHAR,
                OSKAR_CPU, fname_len, status);
    }
    else if (port == 2)
    {
        oskar_mem_append_raw(data->filename_y[i], filename, OSKAR_CHAR,
                OSKAR_CPU, fname_len, status);
    }

    /* Copy X to Y if both ports are the same. */
    if (port == 0)
    {
        oskar_splines_copy(data->y_h_re[i], data->x_h_re[i], status);
        oskar_splines_copy(data->y_h_im[i], data->x_h_im[i], status);
        oskar_splines_copy(data->y_v_re[i], data->x_v_re[i], status);
        oskar_splines_copy(data->y_v_im[i], data->x_v_im[i], status);
    }

    /* Free local arrays. */
    oskar_mem_free(theta, status);
    oskar_mem_free(phi, status);
    oskar_mem_free(h_re, status);
    oskar_mem_free(h_im, status);
    oskar_mem_free(v_re, status);
    oskar_mem_free(v_im, status);
    oskar_mem_free(weight, status);
}