diff --git a/CHANGELOG.md b/CHANGELOG.md index 75bec5a..49a8faa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,10 @@ Changes in each release are listed below. ## 0.8.5 02-Aug-2021 (Pre-release) +* Added helper function get_fine_chan_freqs_hz_array to correlator context and voltage context. * Added metafits_context.num_metafits_fine_chan_freqs & metafits_context.metafits_fine_chan_freqs, providing a vector of sky frequencies for all fine channels. * Added metafits_context.volt_fine_chan_width_hz & metafits_context.num_volt_fine_chans_per_coarse to describe the voltage fine channel configuration. -* Added the above new attributes to equivalent metafits_context struct in FFI. +* Added the above new functions and attributes to equivalent structs in FFI. * Added more badges to github README. ## 0.8.4 15-Jul-2021 (Pre-release) diff --git a/src/correlator_context/mod.rs b/src/correlator_context/mod.rs index f2e081e..0e069c7 100644 --- a/src/correlator_context/mod.rs +++ b/src/correlator_context/mod.rs @@ -363,6 +363,33 @@ impl CorrelatorContext { }) } + /// For a given slice of correlator coarse channel indices, return a vector of the center + /// frequencies for all the fine channels in the given coarse channels + /// + /// # Arguments + /// + /// * `corr_coarse_chan_indices` - a slice containing correlator coarse channel indices + /// for which you want fine channels for. Does not need to be + /// contiguous. + /// + /// + /// # Returns + /// + /// * a vector of f64 containing the centre sky frequencies of all the fine channels for the + /// given coarse channels. + /// + pub fn get_fine_chan_freqs_hz_array(&self, corr_coarse_chan_indices: &[usize]) -> Vec { + CoarseChannel::get_fine_chan_centres_array_hz( + self.mwa_version, + &corr_coarse_chan_indices + .iter() + .map(|c| self.coarse_chans[*c].clone()) + .collect::>(), + self.metafits_context.corr_fine_chan_width_hz, + self.metafits_context.num_corr_fine_chans_per_coarse, + ) + } + /// Read a single timestep for a single coarse channel /// The output visibilities are in order: /// baseline,frequency,pol,r,i diff --git a/src/correlator_context/test.rs b/src/correlator_context/test.rs index d73ab37..cae5291 100644 --- a/src/correlator_context/test.rs +++ b/src/correlator_context/test.rs @@ -1055,3 +1055,68 @@ fn test_get_fits_filename_and_batch_and_hdu() { error ); } + +#[test] +fn test_context_legacy_v1_get_fine_chan_feqs_one_coarse_chan() { + // Open the test legacy file + let metafits_filename = "test_files/1101503312_1_timestep/1101503312.metafits"; + let filename = "test_files/1101503312_1_timestep/1101503312_20141201210818_gpubox01_00.fits"; + + // + // Read the observation using mwalib + // + // Open a context and load in a test metafits and gpubox file + let gpuboxfiles = vec![filename]; + let context = CorrelatorContext::new(&metafits_filename, &gpuboxfiles) + .expect("Failed to create mwalibContext"); + + // Get fine channel freqs + let coarse_channels: Vec = vec![0]; + let fine_chan_freqs: Vec = context.get_fine_chan_freqs_hz_array(&coarse_channels); + + assert_eq!(fine_chan_freqs.len(), 128); + assert!(approx_eq!( + f64, + fine_chan_freqs[0], + 138_880_000.0, + F64Margin::default() + )); +} + +#[test] +fn test_context_legacy_v1_get_fine_chan_feqs_some_coarse_chans() { + // Open the test legacy file + let metafits_filename = "test_files/1101503312_1_timestep/1101503312.metafits"; + let filename = "test_files/1101503312_1_timestep/1101503312_20141201210818_gpubox01_00.fits"; + + // + // Read the observation using mwalib + // + // Open a context and load in a test metafits and gpubox file + let gpuboxfiles = vec![filename]; + let context = CorrelatorContext::new(&metafits_filename, &gpuboxfiles) + .expect("Failed to create mwalibContext"); + + // Get fine channel freqs + let coarse_channels: Vec = vec![10, 20]; + let fine_chan_freqs: Vec = context.get_fine_chan_freqs_hz_array(&coarse_channels); + + assert_eq!(fine_chan_freqs.len(), 256); + assert!(approx_eq!( + f64, + fine_chan_freqs[0], + 151_680_000.0, + F64Margin::default() + )); + + assert!( + approx_eq!( + f64, + fine_chan_freqs[128], + 164_480_000.0, + F64Margin::default() + ), + "calc value: {}", + fine_chan_freqs[128] + ); +} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 1a48b7a..98d5aea 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -8,7 +8,7 @@ This module exists purely for other languages to interface with mwalib. use crate::*; use gpubox_files::GpuboxError; -use libc::{c_char, c_float, c_uchar, c_uint, c_ulong, size_t}; +use libc::{c_char, c_double, c_float, c_uchar, c_uint, c_ulong, size_t}; use std::ffi::*; use std::mem; use std::slice; @@ -602,6 +602,206 @@ pub unsafe extern "C" fn mwalib_correlator_context_read_by_frequency( } } +/// For a given slice of correlator coarse channel indices, return a vector of the center +/// frequencies for all the fine channels in the given coarse channels +/// +/// # Arguments +/// +/// * `correlator_context_ptr` - pointer to an already populated `CorrelatorContext` object. +/// +/// * `corr_coarse_chan_indices_array_ptr` - a pointer to an array containing correlator coarse channel indices +/// for which you want fine channels for. Does not need to be +/// contiguous. +/// +/// * `corr_coarse_chan_indices_array_len` - length of `corr_coarse_chan_indices_array_ptr`. +/// +/// * `out_fine_chan_freq_array_ptr` - pointer to caller-owned and allocated array of doubles to write frequencies into. +/// +/// * `out_fine_chan_freq_array_len` - length of `out_fine_chan_freq_array_ptr`. +/// +/// * `error_message` - pointer to already allocated buffer for any error messages to be returned to the caller. +/// +/// * `error_message_length` - length of error_message char* buffer. +/// +/// +/// # Safety +/// * `error_message` *must* point to an already allocated char* buffer for any error messages. +/// * `correlator_context_ptr` must point to a populated object from the `mwalib_correlator_context_new` function. +/// * Caller *must* call `mwalib_correlator_context_free_read_buffer` function to release the rust memory. +#[no_mangle] +pub unsafe extern "C" fn mwalib_correlator_context_get_fine_chan_freqs_hz_array( + correlator_context_ptr: *mut CorrelatorContext, + corr_coarse_chan_indices_array_ptr: *mut size_t, + corr_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: *const c_char, + error_message_length: size_t, +) -> i32 { + // Load the previously-initialised context and buffer structs. Exit if + // either of these are null. + let corr_context = if correlator_context_ptr.is_null() { + set_error_message( + "mwalib_correlator_context_get_fine_chan_freqs_hz_array() ERROR: null pointer for correlator_context_ptr passed in", + error_message as *mut u8, + error_message_length, + ); + return MWALIB_FAILURE; + } else { + &mut *correlator_context_ptr + }; + + // Don't do anything if the input pointer is null. + if corr_coarse_chan_indices_array_ptr.is_null() { + set_error_message( + "mwalib_correlator_context_get_fine_chan_freqs_hz_array() ERROR: null pointer for corr_coarse_chan_indices_array_ptr passed in", + error_message as *mut u8, + error_message_length, + ); + return MWALIB_FAILURE; + } + + // Get input buffer ready to be passed into rust method + let input_coarse_chan_indices = slice::from_raw_parts_mut( + corr_coarse_chan_indices_array_ptr, + corr_coarse_chan_indices_array_len, + ); + + // Don't do anything if the buffer pointer is null. + if out_fine_chan_freq_array_ptr.is_null() { + set_error_message( + "mwalib_correlator_context_get_fine_chan_freqs_hz_array() ERROR: null pointer for out_fine_chan_freq_array_ptr passed in", + error_message as *mut u8, + error_message_length, + ); + return MWALIB_FAILURE; + } + + // Get output buffer ready + let output_slice = + slice::from_raw_parts_mut(out_fine_chan_freq_array_ptr, out_fine_chan_freq_array_len); + + // Sanity check the length + let expected_output_len = corr_coarse_chan_indices_array_len + * corr_context.metafits_context.num_corr_fine_chans_per_coarse; + if output_slice.len() != expected_output_len { + set_error_message( + &format!("mwalib_correlator_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 u8, + error_message_length, + ); + return MWALIB_FAILURE; + } + + // Read data into provided buffer + let fine_chans = corr_context.get_fine_chan_freqs_hz_array(input_coarse_chan_indices); + + // Write the fine chans back into the provided array + output_slice.clone_from_slice(&fine_chans); + + MWALIB_SUCCESS +} + +/// For a given slice of voltage coarse channel indices, return a vector of the center +/// frequencies for all the fine channels in the given coarse channels +/// +/// # Arguments +/// +/// * `voltage_context_ptr` - pointer to an already populated `VoltageContext` object. +/// +/// * `corr_coarse_chan_indices_array_ptr` - a pointer to an array containing correlator coarse channel indices +/// for which you want fine channels for. Does not need to be +/// contiguous. +/// +/// * `corr_coarse_chan_indices_array_len` - length of `corr_coarse_chan_indices_array_ptr`. +/// +/// * `out_fine_chan_freq_array_ptr` - pointer to caller-owned and allocated array of doubles to write frequencies into. +/// +/// * `out_fine_chan_freq_array_len` - length of `out_fine_chan_freq_array_ptr`. +/// +/// * `error_message` - pointer to already allocated buffer for any error messages to be returned to the caller. +/// +/// * `error_message_length` - length of error_message char* buffer. +/// +/// +/// # Safety +/// * `error_message` *must* point to an already allocated char* buffer for any error messages. +/// * `correlator_context_ptr` must point to a populated object from the `mwalib_correlator_context_new` function. +/// * Caller *must* call `mwalib_correlator_context_free_read_buffer` function to release the rust memory. +#[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: *const c_char, + error_message_length: size_t, +) -> i32 { + // Load the previously-initialised context and buffer structs. Exit if + // either of these are null. + let volt_context = if voltage_context_ptr.is_null() { + set_error_message( + "mwalib_voltage_context_get_fine_chan_freqs_hz_array() ERROR: null pointer for voltage_context_ptr passed in", + error_message as *mut u8, + error_message_length, + ); + return MWALIB_FAILURE; + } else { + &mut *voltage_context_ptr + }; + + // Don't do anything if the input pointer is null. + if volt_coarse_chan_indices_array_ptr.is_null() { + set_error_message( + "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 u8, + error_message_length, + ); + return MWALIB_FAILURE; + } + + // Get input buffer ready to be passed into rust method + let input_coarse_chan_indices = slice::from_raw_parts_mut( + volt_coarse_chan_indices_array_ptr, + volt_coarse_chan_indices_array_len, + ); + + // Don't do anything if the buffer pointer is null. + if out_fine_chan_freq_array_ptr.is_null() { + set_error_message( + "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 u8, + error_message_length, + ); + return MWALIB_FAILURE; + } + + // Get output buffer ready + let output_slice = + slice::from_raw_parts_mut(out_fine_chan_freq_array_ptr, out_fine_chan_freq_array_len); + + // Sanity check the length + 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_error_message( + &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 u8, + error_message_length, + ); + return MWALIB_FAILURE; + } + + // Read data into provided buffer + let fine_chans = volt_context.get_fine_chan_freqs_hz_array(input_coarse_chan_indices); + + // Write the fine chans back into the provided array + output_slice.clone_from_slice(&fine_chans); + + MWALIB_SUCCESS +} + /// Free a previously-allocated `CorrelatorContext` struct (and it's members). /// /// # Arguments diff --git a/src/ffi/test.rs b/src/ffi/test.rs index 91a3d61..335ccc4 100644 --- a/src/ffi/test.rs +++ b/src/ffi/test.rs @@ -746,7 +746,202 @@ fn test_mwalib_correlator_context_legacy_read_by_frequency_null_buffer() { error_message_length, ); - // Should get non zero return code + // Should get no zero return code + assert_ne!(retval, 0); + } +} + +#[test] +fn test_mwalib_correlator_context_get_fine_chan_freqs_hz_array_valid() { + let correlator_context_ptr: *mut CorrelatorContext = get_test_ffi_correlator_context(); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + let buffer_len = 128; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_correlator_context_get_fine_chan_freqs_hz_array( + correlator_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get zero return code + assert_eq!(retval, 0); + + // Reconstitute the buffer + let ret_buffer: Vec = ffi_boxed_slice_to_array(buffer_ptr, buffer_len); + + // Check values + assert_eq!(ret_buffer.len(), buffer_len); + + assert!(approx_eq!( + f64, + ret_buffer[0], + 138_880_000.0, + F64Margin::default() + )); + } +} + +#[test] +fn test_mwalib_correlator_context_get_fine_chan_freqs_hz_array_invalid_buffer_len() { + let correlator_context_ptr: *mut CorrelatorContext = get_test_ffi_correlator_context(); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + // Invalid buffer - too big + let buffer_len = 129; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_correlator_context_get_fine_chan_freqs_hz_array( + correlator_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + + // + // Invalid buffer - too small + // + let buffer_len = 127; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_correlator_context_get_fine_chan_freqs_hz_array( + correlator_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + } +} + +#[test] +fn test_mwalib_correlator_context_get_fine_chan_freqs_hz_array_null_context() { + let correlator_context_ptr: *mut CorrelatorContext = std::ptr::null_mut(); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + // Null context + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + let buffer_len = 128; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_correlator_context_get_fine_chan_freqs_hz_array( + correlator_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + } +} + +#[test] +fn test_mwalib_correlator_context_get_fine_chan_freqs_hz_array_null_coarse_chans() { + let correlator_context_ptr: *mut CorrelatorContext = get_test_ffi_correlator_context(); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + // Null coarse chans + let chan_indices_len: usize = 1; + let chan_indicies_ptr: *mut usize = std::ptr::null_mut(); + + let buffer_len = 128; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_correlator_context_get_fine_chan_freqs_hz_array( + correlator_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + } +} + +#[test] +fn test_mwalib_correlator_context_get_fine_chan_freqs_hz_array_null_buffer() { + let correlator_context_ptr: *mut CorrelatorContext = get_test_ffi_correlator_context(); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + // Null buffer ptr + let buffer_len = 128; + let buffer_ptr: *mut f64 = std::ptr::null_mut(); + + let retval = mwalib_correlator_context_get_fine_chan_freqs_hz_array( + correlator_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code assert_ne!(retval, 0); } } @@ -1073,6 +1268,205 @@ fn test_mwalib_voltage_context_mwaxv2_read_file_valid() { } } +#[test] +fn test_mwalib_voltage_context_get_fine_chan_freqs_hz_array_valid() { + let voltage_context_ptr: *mut VoltageContext = + get_test_ffi_voltage_context(MWAVersion::VCSLegacyRecombined); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + let buffer_len = 128; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_voltage_context_get_fine_chan_freqs_hz_array( + voltage_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get zero return code + assert_eq!(retval, 0); + + // Reconstitute the buffer + let ret_buffer: Vec = ffi_boxed_slice_to_array(buffer_ptr, buffer_len); + + // Check values + assert_eq!(ret_buffer.len(), buffer_len); + + assert!(approx_eq!( + f64, + ret_buffer[0], + 138_880_000.0, + F64Margin::default() + )); + } +} + +#[test] +fn test_mwalib_voltage_context_get_fine_chan_freqs_hz_array_invalid_buffer_len() { + let voltage_context_ptr: *mut VoltageContext = + get_test_ffi_voltage_context(MWAVersion::VCSLegacyRecombined); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + // Invalid buffer - too big + let buffer_len = 129; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_voltage_context_get_fine_chan_freqs_hz_array( + voltage_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + + // + // Invalid buffer - too small + // + let buffer_len = 127; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_voltage_context_get_fine_chan_freqs_hz_array( + voltage_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + } +} + +#[test] +fn test_mwalib_voltage_context_get_fine_chan_freqs_hz_array_null_context() { + let voltage_context_ptr: *mut VoltageContext = std::ptr::null_mut(); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + // Null context + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + let buffer_len = 128; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_voltage_context_get_fine_chan_freqs_hz_array( + voltage_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + } +} + +#[test] +fn test_mwalib_voltage_context_get_fine_chan_freqs_hz_array_null_coarse_chans() { + let voltage_context_ptr: *mut VoltageContext = + get_test_ffi_voltage_context(MWAVersion::VCSLegacyRecombined); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + // Null coarse chans + let chan_indices_len: usize = 1; + let chan_indicies_ptr: *mut usize = std::ptr::null_mut(); + + let buffer_len = 128; + let buffer: Vec = vec![0.0; buffer_len]; + let buffer_ptr: *mut f64 = ffi_array_to_boxed_slice(buffer); + + let retval = mwalib_voltage_context_get_fine_chan_freqs_hz_array( + voltage_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + } +} + +#[test] +fn test_mwalib_voltage_context_get_fine_chan_freqs_hz_array_null_buffer() { + let voltage_context_ptr: *mut VoltageContext = + get_test_ffi_voltage_context(MWAVersion::VCSLegacyRecombined); + + let error_message_length: size_t = 128; + let error_message = CString::new(" ".repeat(error_message_length)).unwrap(); + let error_message_ptr = error_message.as_ptr() as *const c_char; + + unsafe { + let chan_indices_len: usize = 1; + let chan_indicies: Vec = vec![0]; + let chan_indicies_ptr: *mut usize = ffi_array_to_boxed_slice(chan_indicies); + + // Null buffer ptr + let buffer_len = 128; + let buffer_ptr: *mut f64 = std::ptr::null_mut(); + + let retval = mwalib_voltage_context_get_fine_chan_freqs_hz_array( + voltage_context_ptr, + chan_indicies_ptr, + chan_indices_len, + buffer_ptr, + buffer_len, + error_message_ptr, + error_message_length, + ); + + // Should get non-zero return code + assert_ne!(retval, 0); + } +} + // // Metafits Metadata Tests // diff --git a/src/voltage_context/mod.rs b/src/voltage_context/mod.rs index 227da87..453348b 100644 --- a/src/voltage_context/mod.rs +++ b/src/voltage_context/mod.rs @@ -457,6 +457,33 @@ impl VoltageContext { }) } + /// For a given slice of voltage coarse channel indices, return a vector of the center + /// frequencies for all the fine channels in the given coarse channels + /// + /// # Arguments + /// + /// * `volt_coarse_chan_indices` - a slice containing voltage coarse channel indices + /// for which you want fine channels for. Does not need to be + /// contiguous. + /// + /// + /// # Returns + /// + /// * a vector of f64 containing the centre sky frequencies of all the fine channels for the + /// given coarse channels. + /// + pub fn get_fine_chan_freqs_hz_array(&self, volt_coarse_chan_indices: &[usize]) -> Vec { + CoarseChannel::get_fine_chan_centres_array_hz( + self.mwa_version, + &volt_coarse_chan_indices + .iter() + .map(|c| self.coarse_chans[*c].clone()) + .collect::>(), + self.metafits_context.volt_fine_chan_width_hz, + self.metafits_context.num_volt_fine_chans_per_coarse, + ) + } + /// Validates gps time start and gps seconds count, and returns the end gps time or an Error. /// The gps end second is the START time of the end second, not the END of the second. /// e.g gpstart = 100, count = 1, therefore gpsend = 100. diff --git a/src/voltage_context/test.rs b/src/voltage_context/test.rs index f6cfbdf..62d879d 100644 --- a/src/voltage_context/test.rs +++ b/src/voltage_context/test.rs @@ -7,6 +7,7 @@ Unit tests for voltage context */ #[cfg(test)] use super::*; +use float_cmp::*; use std::fs::File; use std::io::{Error, Write}; use std::sync::Once; @@ -1537,3 +1538,44 @@ fn test_context_read_second_mwaxv2_valid() { 185 ); } + +#[test] +fn test_context_legacy_v1_get_fine_chan_feqs_one_coarse_chan() { + let context = get_test_voltage_context(MWAVersion::VCSLegacyRecombined); + + // Get fine channel freqs + let coarse_channels: Vec = vec![0]; + let fine_chan_freqs: Vec = context.get_fine_chan_freqs_hz_array(&coarse_channels); + + assert_eq!(fine_chan_freqs.len(), 128); + assert!(approx_eq!( + f64, + fine_chan_freqs[0], + 138_880_000.0, + F64Margin::default() + )); +} + +#[test] +fn test_context_legacy_v1_get_fine_chan_feqs_some_coarse_chans() { + let context = get_test_voltage_context(MWAVersion::VCSLegacyRecombined); + + // Get fine channel freqs + let coarse_channels: Vec = vec![10, 20]; + let fine_chan_freqs: Vec = context.get_fine_chan_freqs_hz_array(&coarse_channels); + + assert_eq!(fine_chan_freqs.len(), 256); + assert!(approx_eq!( + f64, + fine_chan_freqs[0], + 151_680_000.0, + F64Margin::default() + )); + + assert!(approx_eq!( + f64, + fine_chan_freqs[128], + 164_480_000.0, + F64Margin::default() + )); +}