use crate::{
antenna::{self},
baseline, calibration_fit, coarse_channel,
ffi::{
ffi_create_c_array, ffi_create_c_string, ffi_free_c_array, ffi_free_rust_c_string,
set_c_string, MWALIB_FAILURE, MWALIB_SUCCESS,
},
rfinput, signal_chain_correction, timestep, voltage_beam, CableDelaysApplied,
CorrelatorContext, GeometricDelaysApplied, MWAMode, MWAVersion, MetafitsContext,
VoltageContext,
};
use libc::size_t;
use std::ffi::{c_char, CStr};
#[repr(C)]
pub struct MetafitsMetadata {
pub global_analogue_attenuation_db: f64,
pub ra_tile_pointing_deg: f64,
pub dec_tile_pointing_deg: f64,
pub ra_phase_center_deg: f64,
pub dec_phase_center_deg: f64,
pub az_deg: f64,
pub alt_deg: f64,
pub za_deg: f64,
pub az_rad: f64,
pub alt_rad: f64,
pub za_rad: f64,
pub sun_alt_deg: f64,
pub sun_distance_deg: f64,
pub moon_distance_deg: f64,
pub jupiter_distance_deg: f64,
pub lst_deg: f64,
pub lst_rad: f64,
pub sched_start_mjd: f64,
pub sched_end_mjd: f64,
pub dut1: f64,
pub corr_int_time_ms: u64,
pub sched_start_unix_time_ms: u64,
pub sched_end_unix_time_ms: u64,
pub sched_start_gps_time_ms: u64,
pub sched_end_gps_time_ms: u64,
pub sched_duration_ms: u64,
pub quack_time_duration_ms: u64,
pub good_time_unix_ms: u64,
pub good_time_gps_ms: u64,
pub hour_angle_string: *mut c_char,
pub grid_name: *mut c_char,
pub creator: *mut c_char,
pub project_id: *mut c_char,
pub obs_name: *mut c_char,
pub calibrator_source: *mut c_char,
pub metafits_filename: *mut c_char,
pub deripple_param: *mut c_char,
pub best_cal_code_ver: *mut c_char,
pub best_cal_fit_timestamp: *mut c_char,
pub best_cal_creator: *mut c_char,
pub receivers: *mut usize,
pub delays: *mut u32,
pub antennas: *mut antenna::ffi::Antenna,
pub rf_inputs: *mut rfinput::ffi::Rfinput,
pub baselines: *mut baseline::ffi::Baseline,
pub metafits_coarse_chans: *mut coarse_channel::ffi::CoarseChannel,
pub metafits_fine_chan_freqs_hz: *mut f64,
pub metafits_timesteps: *mut timestep::ffi::TimeStep,
pub signal_chain_corrections: *mut signal_chain_correction::ffi::SignalChainCorrection,
pub calibration_fits: *mut calibration_fit::ffi::CalibrationFit,
pub metafits_voltage_beams: *mut voltage_beam::ffi::VoltageBeam,
pub obs_id: u32,
pub corr_fine_chan_width_hz: u32,
pub volt_fine_chan_width_hz: u32,
pub obs_bandwidth_hz: u32,
pub coarse_chan_width_hz: u32,
pub centre_freq_hz: u32,
pub best_cal_fit_id: u32,
pub best_cal_obs_id: u32,
pub grid_number: i32,
pub best_cal_fit_iters: u16,
pub best_cal_fit_iter_limit: u16,
pub corr_raw_scale_factor: f32,
pub num_corr_fine_chans_per_coarse: usize,
pub num_volt_fine_chans_per_coarse: usize,
pub num_receivers: usize,
pub num_delays: usize,
pub num_ants: usize,
pub num_rf_inputs: usize,
pub num_ant_pols: usize,
pub num_baselines: usize,
pub num_visibility_pols: usize,
pub num_metafits_voltage_beams: usize,
pub num_metafits_coherent_beams: usize,
pub num_metafits_incoherent_beams: usize,
pub num_metafits_coarse_chans: usize,
pub num_metafits_fine_chan_freqs_hz: usize,
pub num_metafits_timesteps: usize,
pub num_signal_chain_corrections: usize,
pub num_calibration_fits: usize,
pub mwa_version: MWAVersion,
pub mode: MWAMode,
pub geometric_delays_applied: GeometricDelaysApplied,
pub cable_delays_applied: CableDelaysApplied,
pub calibration_delays_and_gains_applied: bool,
pub signal_chain_corrections_applied: bool,
pub calibrator: bool,
pub oversampled: bool,
pub deripple_applied: bool,
pub sched_start_utc: libc::time_t,
pub sched_end_utc: libc::time_t,
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_metafits_metadata_get(
metafits_context_ptr: *mut MetafitsContext,
correlator_context_ptr: *mut CorrelatorContext,
voltage_context_ptr: *mut VoltageContext,
out_metafits_metadata_ptr: *mut *mut MetafitsMetadata,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
let provided = usize::from(!metafits_context_ptr.is_null())
+ usize::from(!correlator_context_ptr.is_null())
+ usize::from(!voltage_context_ptr.is_null());
if provided != 1 {
set_c_string(
"mwalib_metafits_metadata_get() ERROR: pointers for metafits_context_ptr, correlator_context_ptr and/or voltage_context_ptr were passed in. Only one should be provided.",
error_message,
error_message_length,
);
if !out_metafits_metadata_ptr.is_null() {
*out_metafits_metadata_ptr = std::ptr::null_mut();
}
return MWALIB_FAILURE;
}
let metafits_context = if !metafits_context_ptr.is_null() {
&*metafits_context_ptr
} else if !correlator_context_ptr.is_null() {
&(*correlator_context_ptr).metafits_context
} else if !voltage_context_ptr.is_null() {
&(*voltage_context_ptr).metafits_context
} else {
set_c_string(
"mwalib_metafits_metadata_get() ERROR: unable to determine which context pointer was provided.",
error_message,
error_message_length,
);
if !out_metafits_metadata_ptr.is_null() {
*out_metafits_metadata_ptr = std::ptr::null_mut();
}
return MWALIB_FAILURE;
};
let (antennas_ptr, antennas_len) = antenna::ffi::Antenna::populate_array(&metafits_context);
let (baselines_ptr, baselines_len) = baseline::ffi::Baseline::populate_array(&metafits_context);
let (beams_ptr, beams_len) = voltage_beam::ffi::VoltageBeam::populate_array(&metafits_context);
let (calibration_fits_ptr, calibration_fits_len) =
calibration_fit::ffi::CalibrationFit::populate_array(&metafits_context);
let (coarse_channels_ptr, coarse_channels_len) =
coarse_channel::ffi::CoarseChannel::populate_array(&metafits_context.metafits_coarse_chans);
let (timesteps_ptr, timesteps_len) =
timestep::ffi::TimeStep::populate_array(&metafits_context.metafits_timesteps);
let (rfinputs_ptr, rfinputs_len) = rfinput::ffi::Rfinput::populate_array(&metafits_context);
let (signal_chain_corrections_ptr, signal_chain_corrections_len) =
signal_chain_correction::ffi::SignalChainCorrection::populate_array(&metafits_context);
let (receivers_ptr, receivers_len) = ffi_create_c_array(metafits_context.receivers.clone());
let (delays_ptr, delays_len) = ffi_create_c_array(metafits_context.delays.clone());
let (metafits_fine_chan_freqs_hz_ptr, metafits_fine_chan_freqs_hz_len) =
ffi_create_c_array(metafits_context.metafits_fine_chan_freqs_hz.clone());
let out_metadata = {
let MetafitsContext {
mwa_version,
obs_id,
sched_start_gps_time_ms,
sched_end_gps_time_ms,
sched_start_unix_time_ms,
sched_end_unix_time_ms,
sched_start_utc,
sched_end_utc,
sched_start_mjd,
sched_end_mjd,
sched_duration_ms,
dut1,
ra_tile_pointing_degrees,
dec_tile_pointing_degrees,
ra_phase_center_degrees,
dec_phase_center_degrees,
az_deg,
alt_deg,
za_deg,
az_rad,
alt_rad,
za_rad,
sun_alt_deg,
sun_distance_deg,
moon_distance_deg,
jupiter_distance_deg,
lst_deg: lst_degrees,
lst_rad: lst_radians,
hour_angle_string,
grid_name,
grid_number,
creator,
project_id,
obs_name,
mode,
geometric_delays_applied,
cable_delays_applied,
calibration_delays_and_gains_applied,
signal_chain_corrections_applied,
corr_fine_chan_width_hz,
corr_int_time_ms,
corr_raw_scale_factor,
num_corr_fine_chans_per_coarse,
volt_fine_chan_width_hz,
num_volt_fine_chans_per_coarse,
receivers: _,
num_receivers: _,
delays: _,
num_delays: _,
calibrator,
calibrator_source,
global_analogue_attenuation_db,
quack_time_duration_ms,
good_time_unix_ms,
good_time_gps_ms,
num_ants: _,
antennas: _, num_rf_inputs: _,
rf_inputs: _, num_ant_pols,
num_metafits_timesteps: _,
metafits_timesteps: _, num_metafits_coarse_chans: _,
metafits_coarse_chans: _, num_metafits_fine_chan_freqs: _,
metafits_fine_chan_freqs_hz: _,
obs_bandwidth_hz,
coarse_chan_width_hz,
centre_freq_hz,
num_baselines: _,
baselines: _, num_visibility_pols,
metafits_filename,
oversampled,
deripple_applied,
deripple_param,
best_cal_fit_id,
best_cal_obs_id,
best_cal_code_ver,
best_cal_fit_timestamp,
best_cal_creator,
best_cal_fit_iters,
best_cal_fit_iter_limit,
signal_chain_corrections: _, num_signal_chain_corrections: _,
calibration_fits: _, num_calibration_fits: _,
metafits_voltage_beams: _, num_metafits_voltage_beams: _,
num_metafits_coherent_beams,
num_metafits_incoherent_beams,
} = metafits_context;
MetafitsMetadata {
mwa_version: mwa_version.unwrap(),
obs_id: *obs_id,
global_analogue_attenuation_db: *global_analogue_attenuation_db,
ra_tile_pointing_deg: *ra_tile_pointing_degrees,
dec_tile_pointing_deg: *dec_tile_pointing_degrees,
ra_phase_center_deg: (*ra_phase_center_degrees).unwrap_or(f64::NAN),
dec_phase_center_deg: (*dec_phase_center_degrees).unwrap_or(f64::NAN),
az_deg: *az_deg,
alt_deg: *alt_deg,
za_deg: *za_deg,
az_rad: *az_rad,
alt_rad: *alt_rad,
za_rad: *za_rad,
sun_alt_deg: (*sun_alt_deg).unwrap_or(f64::NAN),
sun_distance_deg: (*sun_distance_deg).unwrap_or(f64::NAN),
moon_distance_deg: (*moon_distance_deg).unwrap_or(f64::NAN),
jupiter_distance_deg: (*jupiter_distance_deg).unwrap_or(f64::NAN),
lst_deg: *lst_degrees,
lst_rad: *lst_radians,
hour_angle_string: ffi_create_c_string(hour_angle_string),
grid_name: ffi_create_c_string(grid_name),
grid_number: *grid_number,
creator: ffi_create_c_string(creator),
project_id: ffi_create_c_string(project_id),
obs_name: ffi_create_c_string(obs_name),
mode: *mode,
geometric_delays_applied: *geometric_delays_applied,
cable_delays_applied: *cable_delays_applied,
calibration_delays_and_gains_applied: *calibration_delays_and_gains_applied,
signal_chain_corrections_applied: *signal_chain_corrections_applied,
corr_fine_chan_width_hz: *corr_fine_chan_width_hz,
corr_int_time_ms: *corr_int_time_ms,
corr_raw_scale_factor: *corr_raw_scale_factor,
num_corr_fine_chans_per_coarse: *num_corr_fine_chans_per_coarse,
volt_fine_chan_width_hz: *volt_fine_chan_width_hz,
num_volt_fine_chans_per_coarse: *num_volt_fine_chans_per_coarse,
receivers: receivers_ptr,
num_receivers: receivers_len,
delays: delays_ptr,
num_delays: delays_len,
calibrator: *calibrator,
calibrator_source: ffi_create_c_string(calibrator_source),
sched_start_utc: sched_start_utc.timestamp(),
sched_end_utc: sched_end_utc.timestamp(),
sched_start_mjd: *sched_start_mjd,
sched_end_mjd: *sched_end_mjd,
sched_start_unix_time_ms: *sched_start_unix_time_ms,
sched_end_unix_time_ms: *sched_end_unix_time_ms,
sched_start_gps_time_ms: *sched_start_gps_time_ms,
sched_end_gps_time_ms: *sched_end_gps_time_ms,
sched_duration_ms: *sched_duration_ms,
dut1: dut1.unwrap_or(0.0),
quack_time_duration_ms: *quack_time_duration_ms,
good_time_unix_ms: *good_time_unix_ms,
good_time_gps_ms: *good_time_gps_ms,
num_ants: antennas_len,
antennas: antennas_ptr,
num_rf_inputs: rfinputs_len,
rf_inputs: rfinputs_ptr,
num_ant_pols: *num_ant_pols,
num_baselines: baselines_len,
baselines: baselines_ptr,
num_visibility_pols: *num_visibility_pols,
num_metafits_coarse_chans: coarse_channels_len,
metafits_coarse_chans: coarse_channels_ptr,
num_metafits_fine_chan_freqs_hz: metafits_fine_chan_freqs_hz_len,
metafits_fine_chan_freqs_hz: metafits_fine_chan_freqs_hz_ptr,
num_metafits_timesteps: timesteps_len,
metafits_timesteps: timesteps_ptr,
obs_bandwidth_hz: *obs_bandwidth_hz,
coarse_chan_width_hz: *coarse_chan_width_hz,
centre_freq_hz: *centre_freq_hz,
metafits_filename: ffi_create_c_string(metafits_filename),
oversampled: *oversampled,
deripple_applied: *deripple_applied,
deripple_param: ffi_create_c_string(deripple_param),
best_cal_fit_id: best_cal_fit_id.unwrap_or_default(),
best_cal_obs_id: best_cal_obs_id.unwrap_or_default(),
best_cal_code_ver: ffi_create_c_string(
best_cal_code_ver.as_deref().unwrap_or_default(),
),
best_cal_fit_timestamp: ffi_create_c_string(
best_cal_fit_timestamp.as_deref().unwrap_or_default(),
),
best_cal_creator: ffi_create_c_string(best_cal_creator.as_deref().unwrap_or_default()),
best_cal_fit_iters: best_cal_fit_iters.unwrap_or_default(),
best_cal_fit_iter_limit: best_cal_fit_iter_limit.unwrap_or_default(),
signal_chain_corrections: signal_chain_corrections_ptr,
num_signal_chain_corrections: signal_chain_corrections_len,
calibration_fits: calibration_fits_ptr,
num_calibration_fits: calibration_fits_len,
num_metafits_voltage_beams: beams_len,
metafits_voltage_beams: beams_ptr,
num_metafits_coherent_beams: *num_metafits_coherent_beams,
num_metafits_incoherent_beams: *num_metafits_incoherent_beams,
}
};
if !out_metafits_metadata_ptr.is_null() {
*out_metafits_metadata_ptr = Box::into_raw(Box::new(out_metadata));
return MWALIB_SUCCESS;
} else {
set_c_string(
"mwalib_metafits_metadata_get() ERROR: out_metafits_metadata_ptr was NULL.",
error_message,
error_message_length,
);
return MWALIB_FAILURE;
}
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_metafits_metadata_free(m_ptr: *mut MetafitsMetadata) -> i32 {
if m_ptr.is_null() {
return MWALIB_SUCCESS;
}
let boxed: Box<MetafitsMetadata> = Box::from_raw(m_ptr);
antenna::ffi::Antenna::destroy_array(boxed.antennas, boxed.num_ants);
baseline::ffi::Baseline::destroy_array(boxed.baselines, boxed.num_baselines);
voltage_beam::ffi::VoltageBeam::destroy_array(
boxed.metafits_voltage_beams,
boxed.num_metafits_voltage_beams,
);
calibration_fit::ffi::CalibrationFit::destroy_array(
boxed.calibration_fits,
boxed.num_calibration_fits,
);
coarse_channel::ffi::CoarseChannel::destroy_array(
boxed.metafits_coarse_chans,
boxed.num_metafits_coarse_chans,
);
rfinput::ffi::Rfinput::destroy_array(boxed.rf_inputs, boxed.num_rf_inputs);
signal_chain_correction::ffi::SignalChainCorrection::destroy_array(
boxed.signal_chain_corrections,
boxed.num_signal_chain_corrections,
);
timestep::ffi::TimeStep::destroy_array(boxed.metafits_timesteps, boxed.num_metafits_timesteps);
ffi_free_c_array(boxed.delays, boxed.num_delays);
ffi_free_c_array(
boxed.metafits_fine_chan_freqs_hz,
boxed.num_metafits_fine_chan_freqs_hz,
);
ffi_free_c_array(boxed.receivers, boxed.num_receivers);
ffi_free_rust_c_string(boxed.hour_angle_string);
ffi_free_rust_c_string(boxed.grid_name);
ffi_free_rust_c_string(boxed.creator);
ffi_free_rust_c_string(boxed.project_id);
ffi_free_rust_c_string(boxed.obs_name);
ffi_free_rust_c_string(boxed.calibrator_source);
ffi_free_rust_c_string(boxed.metafits_filename);
ffi_free_rust_c_string(boxed.deripple_param);
ffi_free_rust_c_string(boxed.best_cal_code_ver);
ffi_free_rust_c_string(boxed.best_cal_fit_timestamp);
ffi_free_rust_c_string(boxed.best_cal_creator);
drop(boxed);
MWALIB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_metafits_context_new(
metafits_filename: *const c_char,
mwa_version: MWAVersion,
out_metafits_context_ptr: *mut *mut MetafitsContext,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
if error_message.is_null() {
set_c_string(
"mwalib_metafits_context_new(): error_message is NULL",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
if metafits_filename.is_null() {
set_c_string(
"mwalib_metafits_context_new(): metafits_filename is NULL",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
let m = match CStr::from_ptr(metafits_filename).to_str() {
Ok(s) => s.to_owned(),
Err(_) => {
set_c_string(
"invalid UTF-8 in metafits_filename",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
};
let context = match MetafitsContext::new(m, Some(mwa_version)) {
Ok(c) => c,
Err(e) => {
set_c_string(
&format!("{}", e),
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
};
*out_metafits_context_ptr = Box::into_raw(Box::new(context));
MWALIB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_metafits_context_new2(
metafits_filename: *const c_char,
out_metafits_context_ptr: *mut *mut MetafitsContext,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
if error_message.is_null() {
set_c_string(
"mwalib_metafits_context_new(): error_message is NULL",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
if metafits_filename.is_null() {
set_c_string(
"mwalib_metafits_context_new(): metafits_filename is NULL",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
let m = match CStr::from_ptr(metafits_filename).to_str() {
Ok(s) => s.to_owned(),
Err(_) => {
set_c_string(
"invalid UTF-8 in metafits_filename",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
};
let context = match MetafitsContext::new(m, None) {
Ok(c) => c,
Err(e) => {
set_c_string(
&format!("{}", e),
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
};
*out_metafits_context_ptr = Box::into_raw(Box::new(context));
MWALIB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_metafits_get_expected_volt_filename(
metafits_context_ptr: *const MetafitsContext,
metafits_timestep_index: usize,
metafits_coarse_chan_index: usize,
out_filename_ptr: *mut c_char,
out_filename_len: size_t,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
if metafits_context_ptr.is_null() {
set_c_string(
"mwalib_metafits_get_expected_voltage_filename() ERROR: null pointer for metafits_context_ptr passed in",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
let context = &*metafits_context_ptr;
match context
.generate_expected_volt_filename(metafits_timestep_index, metafits_coarse_chan_index)
{
Err(e) => {
set_c_string(
&e.to_string(),
error_message as *mut c_char,
error_message_length,
);
MWALIB_FAILURE
}
Ok(s) => {
set_c_string(&s, out_filename_ptr as *mut c_char, out_filename_len);
MWALIB_SUCCESS
}
}
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_metafits_context_display(
metafits_context_ptr: *const MetafitsContext,
out_buffer_ptr: *mut c_char,
out_buffer_len: usize,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
if metafits_context_ptr.is_null() {
set_c_string(
"mwalib_metafits_context_display() ERROR: null pointer for metafits_context_ptr passed in",
error_message,
error_message_length,
);
return MWALIB_FAILURE;
}
let context = &*metafits_context_ptr;
let output = format!("{}", context);
set_c_string(&output, out_buffer_ptr, out_buffer_len);
MWALIB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_metafits_context_free(
metafits_context_ptr: *mut MetafitsContext,
) -> i32 {
if metafits_context_ptr.is_null() {
return MWALIB_SUCCESS;
}
drop(Box::from_raw(metafits_context_ptr));
MWALIB_SUCCESS
}