use std::{
ffi::{c_char, c_double, c_schar, CStr},
slice,
};
use libc::size_t;
use crate::{
coarse_channel,
ffi::{
ffi_create_c_array, set_c_string, MWALIB_FAILURE, MWALIB_NO_DATA_FOR_TIMESTEP_COARSECHAN,
MWALIB_SUCCESS,
},
timestep, voltage_context, MWAVersion, VoltageContext, VoltageFileError,
};
#[repr(C)]
pub struct VoltageMetadata {
pub timestep_duration_ms: u64,
pub common_start_unix_time_ms: u64,
pub common_end_unix_time_ms: u64,
pub common_start_gps_time_ms: u64,
pub common_end_gps_time_ms: u64,
pub common_duration_ms: u64,
pub common_good_start_unix_time_ms: u64,
pub common_good_end_unix_time_ms: u64,
pub common_good_start_gps_time_ms: u64,
pub common_good_end_gps_time_ms: u64,
pub common_good_duration_ms: u64,
pub sample_size_bytes: u64,
pub voltage_block_size_bytes: u64,
pub delay_block_size_bytes: u64,
pub data_file_header_size_bytes: u64,
pub expected_voltage_data_file_size_bytes: u64,
pub timesteps: *mut timestep::ffi::TimeStep,
pub coarse_chans: *mut coarse_channel::ffi::CoarseChannel,
pub common_timestep_indices: *mut usize,
pub common_coarse_chan_indices: *mut usize,
pub common_good_timestep_indices: *mut usize,
pub common_good_coarse_chan_indices: *mut usize,
pub provided_timestep_indices: *mut usize,
pub provided_coarse_chan_indices: *mut usize,
pub num_timesteps: usize,
pub num_coarse_chans: usize,
pub num_common_timesteps: usize,
pub num_common_coarse_chans: usize,
pub num_common_good_timesteps: usize,
pub num_common_good_coarse_chans: usize,
pub num_provided_timesteps: usize,
pub num_provided_coarse_chans: usize,
pub num_fine_chans_per_coarse: usize,
pub num_voltage_blocks_per_timestep: usize,
pub num_voltage_blocks_per_second: usize,
pub num_samples_per_voltage_block: usize,
pub common_bandwidth_hz: u32,
pub common_good_bandwidth_hz: u32,
pub coarse_chan_width_hz: u32,
pub fine_chan_width_hz: u32,
pub mwa_version: MWAVersion,
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_voltage_metadata_get(
voltage_context_ptr: *mut voltage_context::VoltageContext,
out_voltage_metadata_ptr: *mut *mut VoltageMetadata,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
if voltage_context_ptr.is_null() {
set_c_string(
"mwalib_voltage_metadata_get() ERROR: Warning: null pointer for voltage_context_ptr passed in",
error_message,
error_message_length,
);
if !out_voltage_metadata_ptr.is_null() {
*out_voltage_metadata_ptr = std::ptr::null_mut();
}
return MWALIB_FAILURE;
}
let context = &*voltage_context_ptr;
let (coarse_channels_ptr, coarse_channels_len) =
coarse_channel::ffi::CoarseChannel::populate_array(&context.coarse_chans);
let (timesteps_ptr, timesteps_len) =
timestep::ffi::TimeStep::populate_array(&context.timesteps);
let out_metadata = {
let voltage_context::VoltageContext {
metafits_context: _, mwa_version,
num_timesteps: _,
timesteps: _, timestep_duration_ms,
num_coarse_chans: _,
coarse_chans: _, common_timestep_indices: _,
num_common_timesteps: _,
common_coarse_chan_indices: _,
num_common_coarse_chans: _,
common_start_unix_time_ms,
common_end_unix_time_ms,
common_start_gps_time_ms,
common_end_gps_time_ms,
common_duration_ms,
common_bandwidth_hz,
common_good_timestep_indices: _,
num_common_good_timesteps: _,
common_good_coarse_chan_indices: _,
num_common_good_coarse_chans: _,
common_good_start_unix_time_ms,
common_good_end_unix_time_ms,
common_good_start_gps_time_ms,
common_good_end_gps_time_ms,
common_good_duration_ms,
common_good_bandwidth_hz,
provided_timestep_indices: _,
num_provided_timesteps: _,
provided_coarse_chan_indices: _,
num_provided_coarse_chans: _,
coarse_chan_width_hz,
fine_chan_width_hz,
num_fine_chans_per_coarse,
sample_size_bytes,
num_voltage_blocks_per_timestep,
num_voltage_blocks_per_second,
num_samples_per_voltage_block,
voltage_block_size_bytes,
delay_block_size_bytes,
data_file_header_size_bytes,
expected_voltage_data_file_size_bytes,
voltage_batches: _, voltage_time_map: _, } = context;
let (common_timestep_indices_ptr, common_timestep_indices_len) =
ffi_create_c_array(context.common_timestep_indices.clone());
let (common_good_timestep_indices_ptr, common_good_timestep_indices_len) =
ffi_create_c_array(context.common_good_timestep_indices.clone());
let (provided_timestep_indices_ptr, provided_timestep_indices_len) =
ffi_create_c_array(context.provided_timestep_indices.clone());
let (common_coarse_chan_indices_ptr, common_coarse_chan_indices_len) =
ffi_create_c_array(context.common_coarse_chan_indices.clone());
let (common_good_coarse_chan_indices_ptr, common_good_coarse_chan_indices_len) =
ffi_create_c_array(context.common_good_coarse_chan_indices.clone());
let (provided_coarse_chan_indices_ptr, provided_coarse_chan_indices_len) =
ffi_create_c_array(context.provided_coarse_chan_indices.clone());
VoltageMetadata {
mwa_version: *mwa_version,
timesteps: timesteps_ptr,
num_timesteps: timesteps_len,
timestep_duration_ms: *timestep_duration_ms,
coarse_chans: coarse_channels_ptr,
num_coarse_chans: coarse_channels_len,
num_common_timesteps: common_timestep_indices_len,
common_timestep_indices: common_timestep_indices_ptr,
num_common_coarse_chans: common_coarse_chan_indices_len,
common_coarse_chan_indices: common_coarse_chan_indices_ptr,
common_start_unix_time_ms: *common_start_unix_time_ms,
common_end_unix_time_ms: *common_end_unix_time_ms,
common_start_gps_time_ms: *common_start_gps_time_ms,
common_end_gps_time_ms: *common_end_gps_time_ms,
common_duration_ms: *common_duration_ms,
common_bandwidth_hz: *common_bandwidth_hz,
num_common_good_timesteps: common_good_timestep_indices_len,
common_good_timestep_indices: common_good_timestep_indices_ptr,
num_common_good_coarse_chans: common_good_coarse_chan_indices_len,
common_good_coarse_chan_indices: common_good_coarse_chan_indices_ptr,
common_good_start_unix_time_ms: *common_good_start_unix_time_ms,
common_good_end_unix_time_ms: *common_good_end_unix_time_ms,
common_good_start_gps_time_ms: *common_good_start_gps_time_ms,
common_good_end_gps_time_ms: *common_good_end_gps_time_ms,
common_good_duration_ms: *common_good_duration_ms,
common_good_bandwidth_hz: *common_good_bandwidth_hz,
num_provided_timesteps: provided_timestep_indices_len,
provided_timestep_indices: provided_timestep_indices_ptr,
num_provided_coarse_chans: provided_coarse_chan_indices_len,
provided_coarse_chan_indices: provided_coarse_chan_indices_ptr,
coarse_chan_width_hz: *coarse_chan_width_hz,
fine_chan_width_hz: *fine_chan_width_hz,
num_fine_chans_per_coarse: *num_fine_chans_per_coarse,
sample_size_bytes: *sample_size_bytes,
num_voltage_blocks_per_timestep: *num_voltage_blocks_per_timestep,
num_voltage_blocks_per_second: *num_voltage_blocks_per_second,
num_samples_per_voltage_block: *num_samples_per_voltage_block,
voltage_block_size_bytes: *voltage_block_size_bytes,
delay_block_size_bytes: *delay_block_size_bytes,
data_file_header_size_bytes: *data_file_header_size_bytes,
expected_voltage_data_file_size_bytes: *expected_voltage_data_file_size_bytes,
}
};
if !out_voltage_metadata_ptr.is_null() {
*out_voltage_metadata_ptr = Box::into_raw(Box::new(out_metadata));
return MWALIB_SUCCESS;
} else {
set_c_string(
"mwalib_voltage_metadata_get() ERROR: out_voltage_metadata_ptr was NULL.",
error_message,
error_message_length,
);
return MWALIB_FAILURE;
}
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_voltage_metadata_free(
voltage_metadata_ptr: *mut VoltageMetadata,
) -> i32 {
if voltage_metadata_ptr.is_null() {
return MWALIB_SUCCESS;
}
if !(*voltage_metadata_ptr).coarse_chans.is_null() {
let slice: &mut [coarse_channel::ffi::CoarseChannel] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).coarse_chans,
(*voltage_metadata_ptr).num_coarse_chans,
);
drop(Box::from_raw(slice));
}
if !(*voltage_metadata_ptr).timesteps.is_null() {
let slice: &mut [timestep::ffi::TimeStep] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).timesteps,
(*voltage_metadata_ptr).num_timesteps,
);
drop(Box::from_raw(slice));
}
if !(*voltage_metadata_ptr).common_timestep_indices.is_null() {
let slice: &mut [usize] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).common_timestep_indices,
(*voltage_metadata_ptr).num_common_timesteps,
);
drop(Box::from_raw(slice));
}
if !(*voltage_metadata_ptr).common_coarse_chan_indices.is_null() {
let slice: &mut [usize] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).common_coarse_chan_indices,
(*voltage_metadata_ptr).num_common_coarse_chans,
);
drop(Box::from_raw(slice));
}
if !(*voltage_metadata_ptr)
.common_good_timestep_indices
.is_null()
{
let slice: &mut [usize] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).common_good_timestep_indices,
(*voltage_metadata_ptr).num_common_good_timesteps,
);
drop(Box::from_raw(slice));
}
if !(*voltage_metadata_ptr)
.common_good_coarse_chan_indices
.is_null()
{
let slice: &mut [usize] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).common_good_coarse_chan_indices,
(*voltage_metadata_ptr).num_common_good_coarse_chans,
);
drop(Box::from_raw(slice));
}
if !(*voltage_metadata_ptr).provided_timestep_indices.is_null() {
let slice: &mut [usize] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).provided_timestep_indices,
(*voltage_metadata_ptr).num_provided_timesteps,
);
drop(Box::from_raw(slice));
}
if !(*voltage_metadata_ptr)
.provided_coarse_chan_indices
.is_null()
{
let slice: &mut [usize] = slice::from_raw_parts_mut(
(*voltage_metadata_ptr).provided_coarse_chan_indices,
(*voltage_metadata_ptr).num_provided_coarse_chans,
);
drop(Box::from_raw(slice));
}
drop(Box::from_raw(voltage_metadata_ptr));
MWALIB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_voltage_context_get_fine_chan_freqs_hz_array(
voltage_context_ptr: *mut VoltageContext,
volt_coarse_chan_indices_array_ptr: *mut size_t,
volt_coarse_chan_indices_array_len: size_t,
out_fine_chan_freq_array_ptr: *mut c_double,
out_fine_chan_freq_array_len: size_t,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
let volt_context = if voltage_context_ptr.is_null() {
set_c_string(
"mwalib_voltage_context_get_fine_chan_freqs_hz_array() ERROR: null pointer for voltage_context_ptr passed in",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
} else {
&mut *voltage_context_ptr
};
if volt_coarse_chan_indices_array_ptr.is_null() {
set_c_string(
"mwalib_voltage_context_get_fine_chan_freqs_hz_array() ERROR: null pointer for volt_coarse_chan_indices_array_ptr passed in",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
let input_coarse_chan_indices = slice::from_raw_parts_mut(
volt_coarse_chan_indices_array_ptr,
volt_coarse_chan_indices_array_len,
);
if out_fine_chan_freq_array_ptr.is_null() {
set_c_string(
"mwalib_voltage_context_get_fine_chan_freqs_hz_array() ERROR: null pointer for out_fine_chan_freq_array_ptr passed in",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
let output_slice =
slice::from_raw_parts_mut(out_fine_chan_freq_array_ptr, out_fine_chan_freq_array_len);
let expected_output_len = volt_coarse_chan_indices_array_len
* volt_context.metafits_context.num_corr_fine_chans_per_coarse;
if output_slice.len() != expected_output_len {
set_c_string(
&format!("mwalib_voltage_context_get_fine_chan_freqs_hz_array() ERROR: number of elements in out_fine_chan_freq_array_ptr does not match expected value {}", expected_output_len),
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
let fine_chans = volt_context.get_fine_chan_freqs_hz_array(input_coarse_chan_indices);
output_slice.clone_from_slice(&fine_chans);
MWALIB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_voltage_context_new(
metafits_filename: *const c_char,
voltage_filenames: *mut *const c_char,
voltage_file_count: size_t,
out_voltage_context_ptr: &mut *mut VoltageContext,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
let m = match CStr::from_ptr(metafits_filename).to_str() {
Ok(s) => s,
Err(_) => {
set_c_string(
"invalid UTF-8 in metafits_filename",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
};
let voltage_slice = slice::from_raw_parts(voltage_filenames, voltage_file_count);
let mut voltage_files = Vec::with_capacity(voltage_file_count);
for v in voltage_slice {
let s = match CStr::from_ptr(*v).to_str() {
Ok(s) => s,
Err(_) => {
set_c_string(
"invalid UTF-8 in voltage_filename",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
};
voltage_files.push(s.to_string())
}
let context = match VoltageContext::new(m, &voltage_files) {
Ok(c) => c,
Err(e) => {
set_c_string(
&format!("{}", e),
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
}
};
*out_voltage_context_ptr = Box::into_raw(Box::new(context));
MWALIB_SUCCESS
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_voltage_context_display(
voltage_context_ptr: *const VoltageContext,
out_buffer_ptr: *mut c_char,
out_buffer_len: usize,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
if voltage_context_ptr.is_null() {
set_c_string(
"mwalib_voltage_context() ERROR: null pointer for voltage_context_ptr passed in",
error_message,
error_message_length,
);
return MWALIB_FAILURE;
}
let context = &*voltage_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_voltage_context_read_file(
voltage_context_ptr: *mut VoltageContext,
voltage_timestep_index: size_t,
voltage_coarse_chan_index: size_t,
buffer_ptr: *mut c_schar,
buffer_len: size_t,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
let voltage_context = if voltage_context_ptr.is_null() {
set_c_string(
"mwalib_voltage_context_read_by_file() ERROR: null pointer for voltage_context_ptr passed in",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
} else {
&mut *voltage_context_ptr
};
if buffer_ptr.is_null() {
return MWALIB_FAILURE;
}
let output_slice: &mut [i8] = slice::from_raw_parts_mut(buffer_ptr, buffer_len);
match voltage_context.read_file(
voltage_timestep_index,
voltage_coarse_chan_index,
output_slice,
) {
Ok(_) => MWALIB_SUCCESS,
Err(e) => match e {
VoltageFileError::NoDataForTimeStepCoarseChannel {
timestep_index: _,
coarse_chan_index: _,
} => {
set_c_string(
&format!("{}", e),
error_message as *mut c_char,
error_message_length,
);
MWALIB_NO_DATA_FOR_TIMESTEP_COARSECHAN
}
_ => {
set_c_string(
&format!("{}", e),
error_message as *mut c_char,
error_message_length,
);
MWALIB_FAILURE
}
},
}
}
#[no_mangle]
pub unsafe extern "C" fn mwalib_voltage_context_read_second(
voltage_context_ptr: *mut VoltageContext,
gps_second_start: u64,
gps_second_count: size_t,
voltage_coarse_chan_index: size_t,
buffer_ptr: *mut c_schar,
buffer_len: size_t,
error_message: *mut c_char,
error_message_length: size_t,
) -> i32 {
let voltage_context = if voltage_context_ptr.is_null() {
set_c_string(
"mwalib_voltage_context_read_by_file() ERROR: null pointer for voltage_context_ptr passed in",
error_message as *mut c_char,
error_message_length,
);
return MWALIB_FAILURE;
} else {
&mut *voltage_context_ptr
};
if buffer_ptr.is_null() {
return MWALIB_FAILURE;
}
let output_slice: &mut [i8] = slice::from_raw_parts_mut(buffer_ptr, buffer_len);
match voltage_context.read_second(
gps_second_start,
gps_second_count,
voltage_coarse_chan_index,
output_slice,
) {
Ok(_) => MWALIB_SUCCESS,
Err(e) => match e {
VoltageFileError::NoDataForTimeStepCoarseChannel {
timestep_index: _,
coarse_chan_index: _,
} => {
set_c_string(
&format!("{}", e),
error_message as *mut c_char,
error_message_length,
);
MWALIB_NO_DATA_FOR_TIMESTEP_COARSECHAN
}
_ => {
set_c_string(
&format!("{}", e),
error_message as *mut c_char,
error_message_length,
);
MWALIB_FAILURE
}
},
}
}
#[no_mangle]
#[allow(unused_must_use)]
pub unsafe extern "C" fn mwalib_voltage_context_free(
voltage_context_ptr: *mut crate::VoltageContext,
) -> i32 {
if voltage_context_ptr.is_null() {
return MWALIB_SUCCESS;
}
drop(Box::from_raw(voltage_context_ptr));
MWALIB_SUCCESS
}