diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f3dcdb..75bec5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Changes in each release are listed below. +## 0.8.5 02-Aug-2021 (Pre-release) +* 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 more badges to github README. + ## 0.8.4 15-Jul-2021 (Pre-release) * mwalib legacy autocorrelations (where ant1==ant2) are now conjugated with respect to previous versions. diff --git a/Cargo.toml b/Cargo.toml index a076789..cb51e40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mwalib" -version = "0.8.4" +version = "0.8.5" homepage = "https://github.com/MWATelescope/mwalib" repository = "https://github.com/MWATelescope/mwalib" readme = "README.md" diff --git a/src/coarse_channel/mod.rs b/src/coarse_channel/mod.rs index 7b2a207..90dd7a6 100644 --- a/src/coarse_channel/mod.rs +++ b/src/coarse_channel/mod.rs @@ -225,7 +225,7 @@ impl CoarseChannel { _ => match voltage_time_map { Some(v) => { if let Some((_, channel_map)) = v.iter().next() { - if channel_map.contains_key(&rec_chan_number) { + if channel_map.contains_key(rec_chan_number) { coarse_chans.push(CoarseChannel::new( correlator_chan_number, *rec_chan_number, @@ -264,7 +264,7 @@ impl CoarseChannel { match gpubox_time_map { Some(g) => { if let Some((_, channel_map)) = g.iter().next() { - if channel_map.contains_key(&rec_chan_number) { + if channel_map.contains_key(rec_chan_number) { coarse_chans.push(CoarseChannel::new( correlator_chan_number, *rec_chan_number, @@ -277,7 +277,7 @@ impl CoarseChannel { _ => match voltage_time_map { Some(v) => { if let Some((_, channel_map)) = v.iter().next() { - if channel_map.contains_key(&rec_chan_number) { + if channel_map.contains_key(rec_chan_number) { coarse_chans.push(CoarseChannel::new( correlator_chan_number, *rec_chan_number, @@ -337,16 +337,14 @@ impl CoarseChannel { coarse_chan_indices } - /// Calculate the centre frequency of first fine channel of coarse channel. + /// Calculate the centre frequency of each fine channel of this coarse channel. /// /// /// # Arguments /// /// * `mwa_version` - The version of the MWA is in use. /// - /// * `coarse_chan_width_hz` - Width in Hz of the coarse channel. - /// - /// * `coarse_chan_centre_hz` - Center in sky frequency of this coarse channel. + /// * `coarse_channels` - Vector of populated Coarse Channels. /// /// * `fine_chan_width_hz` - Fine channel width in Hz. /// @@ -356,23 +354,21 @@ impl CoarseChannel { /// /// * The centre frequency of the first fine channel of the coarse channel. /// - pub fn get_first_fine_chan_centre_hz( + pub fn get_fine_chan_centres_array_hz( mwa_version: MWAVersion, - coarse_chan_width_hz: u32, - coarse_chan_centre_hz: u32, - fine_chan_width_hz: u64, + coarse_channels: &[CoarseChannel], + fine_chan_width_hz: u32, num_fine_chans_per_coarse: usize, - ) -> f64 { + ) -> Vec { // Firstly calculate the offset // For Legacy MWA, the offset is only needed if the fine channel width is 20 or 40kHz. let offset_hz = match mwa_version { MWAVersion::CorrLegacy | MWAVersion::CorrOldLegacy | MWAVersion::VCSLegacyRecombined => match num_fine_chans_per_coarse { - 128 => 0.0, // 10 kHz - 64 => 5_000.0, // 20 kHz - 32 => 15_000.0, // 40 Khz - _ => 0.0, + 64 => 5_000.0, // 20 kHz corr mode needs a 5 kHz offset applied + 32 => 15_000.0, // 40 kHz corr mode needs a 15 kHz offset applied + _ => 0.0, // other modes (10kHz) does not need any offset applied }, MWAVersion::CorrMWAXv2 | MWAVersion::VCSMWAXv2 => 0.0, }; @@ -383,16 +379,18 @@ impl CoarseChannel { false => 0.5, // Odd }; - // Now calculate the first fine chan centre hz: - // - // = coarse_chan_centre - (coarse_chan_width/2) // This gets you to the start edge Hz of the coarse channel - // + (odd_even_adjustment * fine_chan_width) // This moves us into the centre of that fine channel - let first_fine_chan_center_hz: f64 = coarse_chan_centre_hz as f64 - - (coarse_chan_width_hz as f64 / 2.0) - + (odd_even_adjustment * fine_chan_width_hz as f64); - - // Apply the offset (if required) - first_fine_chan_center_hz + offset_hz + // Return a vector of f64s which are the fine channel centre frequencies for all the fine channels in [coarse_channels] + coarse_channels + .iter() + .flat_map(|coarse_chan| { + let chan_start_hz = coarse_chan.chan_start_hz; + (0..num_fine_chans_per_coarse).map(move |fine_chan_idx| { + chan_start_hz as f64 + + ((fine_chan_idx as f64 + odd_even_adjustment) * fine_chan_width_hz as f64) + + offset_hz + }) + }) + .collect() } } diff --git a/src/coarse_channel/test.rs b/src/coarse_channel/test.rs index 42b60f7..87e9d46 100644 --- a/src/coarse_channel/test.rs +++ b/src/coarse_channel/test.rs @@ -603,205 +603,317 @@ fn test_get_coarse_chan_indicies() { } #[test] -fn test_get_first_fine_chan_centre_hz_legacy_40khz() { - let coarse_chan_width_hz: u32 = 1_280_000; - let rec_channel_num: usize = 131; - let coarse_chan_centre_hz: u32 = rec_channel_num as u32 * coarse_chan_width_hz; - let fine_chan_width_hz: u64 = 40_000; - let num_fine_chans_per_coarse: usize = - coarse_chan_width_hz as usize / fine_chan_width_hz as usize; - - let calc_first_fine_centre_hz: f64 = CoarseChannel::get_first_fine_chan_centre_hz( - MWAVersion::CorrLegacy, - coarse_chan_width_hz, - coarse_chan_centre_hz, +fn test_get_fine_chan_centres_array_hz_legacy_40khz() { + let metafits_chan_array: Vec<_> = (131..155).collect(); + assert_eq!(metafits_chan_array.len(), 24); + let channel_width = 1_280_000; + let mwa_version: MWAVersion = MWAVersion::CorrLegacy; + + // Process coarse channels + let coarse_chan_array = CoarseChannel::populate_coarse_channels( + mwa_version, + &metafits_chan_array, + channel_width, + None, + None, + ) + .unwrap(); + + let fine_chan_width_hz: u32 = 40_000; + let num_fine_chans_per_coarse: usize = channel_width as usize / fine_chan_width_hz as usize; + + let calc_fine_chan_centre_array_hz = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &coarse_chan_array, fine_chan_width_hz, num_fine_chans_per_coarse, ); + // Check we have the right number of fine channels + assert_eq!( + calc_fine_chan_centre_array_hz.len(), + 24 * num_fine_chans_per_coarse + ); + + // Check values assert!( approx_eq!( f64, 167_055_000.0, - calc_first_fine_centre_hz, + calc_fine_chan_centre_array_hz[0], F64Margin::default() ), "calculated value: {}", - calc_first_fine_centre_hz + calc_fine_chan_centre_array_hz[0] ); } #[test] -fn test_get_first_fine_chan_centre_hz_legacy_20khz() { - let coarse_chan_width_hz: u32 = 1_280_000; - let rec_channel_num: usize = 131; - let coarse_chan_centre_hz: u32 = rec_channel_num as u32 * coarse_chan_width_hz; - let fine_chan_width_hz: u64 = 20_000; - let num_fine_chans_per_coarse: usize = - coarse_chan_width_hz as usize / fine_chan_width_hz as usize; - - let calc_first_fine_centre_hz: f64 = CoarseChannel::get_first_fine_chan_centre_hz( - MWAVersion::CorrLegacy, - coarse_chan_width_hz, - coarse_chan_centre_hz, +fn test_get_fine_chan_centres_array_hz_legacy_20khz() { + let metafits_chan_array: Vec<_> = (131..155).collect(); + let channel_width = 1_280_000; + let mwa_version: MWAVersion = MWAVersion::CorrLegacy; + + // Process coarse channels + let coarse_chan_array = CoarseChannel::populate_coarse_channels( + mwa_version, + &metafits_chan_array, + channel_width, + None, + None, + ) + .unwrap(); + + let fine_chan_width_hz: u32 = 20_000; + let num_fine_chans_per_coarse: usize = channel_width as usize / fine_chan_width_hz as usize; + + let calc_fine_chan_centre_array_hz = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &coarse_chan_array, fine_chan_width_hz, num_fine_chans_per_coarse, ); + // Check we have the right number of fine channels + assert_eq!( + calc_fine_chan_centre_array_hz.len(), + 24 * num_fine_chans_per_coarse + ); + + // Check values assert!( approx_eq!( f64, 167_045_000.0, - calc_first_fine_centre_hz, + calc_fine_chan_centre_array_hz[0], F64Margin::default() ), "calculated value: {}", - calc_first_fine_centre_hz + calc_fine_chan_centre_array_hz[0] ); } #[test] -fn test_get_first_fine_chan_centre_hz_legacy_10khz() { - let coarse_chan_width_hz: u32 = 1_280_000; - let rec_channel_num: usize = 131; - let coarse_chan_centre_hz: u32 = rec_channel_num as u32 * coarse_chan_width_hz; - let fine_chan_width_hz: u64 = 10_000; - let num_fine_chans_per_coarse: usize = - coarse_chan_width_hz as usize / fine_chan_width_hz as usize; - - let calc_first_fine_centre_hz: f64 = CoarseChannel::get_first_fine_chan_centre_hz( - MWAVersion::CorrLegacy, - coarse_chan_width_hz, - coarse_chan_centre_hz, +fn test_get_fine_chan_centres_array_hz_hz_legacy_10khz() { + let metafits_chan_array: Vec<_> = (131..155).collect(); + let channel_width = 1_280_000; + let mwa_version: MWAVersion = MWAVersion::CorrLegacy; + + // Process coarse channels + let coarse_chan_array = CoarseChannel::populate_coarse_channels( + mwa_version, + &metafits_chan_array, + channel_width, + None, + None, + ) + .unwrap(); + + let fine_chan_width_hz: u32 = 10_000; + let num_fine_chans_per_coarse: usize = channel_width as usize / fine_chan_width_hz as usize; + + let calc_fine_chan_centre_array_hz = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &coarse_chan_array, fine_chan_width_hz, num_fine_chans_per_coarse, ); + // Check we have the right number of fine channels + assert_eq!( + calc_fine_chan_centre_array_hz.len(), + 24 * num_fine_chans_per_coarse + ); + + // Check values assert!( approx_eq!( f64, 167_040_000.0, - calc_first_fine_centre_hz, + calc_fine_chan_centre_array_hz[0], F64Margin::default() ), "calculated value: {}", - calc_first_fine_centre_hz + calc_fine_chan_centre_array_hz[0] ); } #[test] -fn test_get_first_fine_chan_centre_hz_mwaxv2_40khz() { - let coarse_chan_width_hz: u32 = 1_280_000; - let rec_channel_num: usize = 131; - let coarse_chan_centre_hz: u32 = rec_channel_num as u32 * coarse_chan_width_hz; - let fine_chan_width_hz: u64 = 40_000; - let num_fine_chans_per_coarse: usize = - coarse_chan_width_hz as usize / fine_chan_width_hz as usize; - - let calc_first_fine_centre_hz: f64 = CoarseChannel::get_first_fine_chan_centre_hz( - MWAVersion::CorrMWAXv2, - coarse_chan_width_hz, - coarse_chan_centre_hz, +fn test_get_fine_chan_centres_array_hz_mwaxv2_40khz() { + let metafits_chan_array: Vec<_> = (131..155).collect(); + let channel_width = 1_280_000; + let mwa_version: MWAVersion = MWAVersion::CorrMWAXv2; + + // Process coarse channels + let coarse_chan_array = CoarseChannel::populate_coarse_channels( + mwa_version, + &metafits_chan_array, + channel_width, + None, + None, + ) + .unwrap(); + + let fine_chan_width_hz: u32 = 40_000; + let num_fine_chans_per_coarse: usize = channel_width as usize / fine_chan_width_hz as usize; + + let calc_fine_chan_centre_array_hz = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &coarse_chan_array, fine_chan_width_hz, num_fine_chans_per_coarse, ); + // Check we have the right number of fine channels + assert_eq!( + calc_fine_chan_centre_array_hz.len(), + 24 * num_fine_chans_per_coarse + ); + + // Check values assert!( approx_eq!( f64, 167_040_000.0, - calc_first_fine_centre_hz, + calc_fine_chan_centre_array_hz[0], F64Margin::default() ), "calculated value: {}", - calc_first_fine_centre_hz + calc_fine_chan_centre_array_hz[0] ); } #[test] -fn test_get_first_fine_chan_centre_hz_mwaxv2_20khz() { - let coarse_chan_width_hz: u32 = 1_280_000; - let rec_channel_num: usize = 131; - let coarse_chan_centre_hz: u32 = rec_channel_num as u32 * coarse_chan_width_hz; - let fine_chan_width_hz: u64 = 20_000; - let num_fine_chans_per_coarse: usize = - coarse_chan_width_hz as usize / fine_chan_width_hz as usize; - - let calc_first_fine_centre_hz: f64 = CoarseChannel::get_first_fine_chan_centre_hz( - MWAVersion::CorrMWAXv2, - coarse_chan_width_hz, - coarse_chan_centre_hz, +fn test_get_fine_chan_centres_array_hz_mwaxv2_20khz() { + let metafits_chan_array: Vec<_> = (131..155).collect(); + let channel_width = 1_280_000; + let mwa_version: MWAVersion = MWAVersion::CorrMWAXv2; + + // Process coarse channels + let coarse_chan_array = CoarseChannel::populate_coarse_channels( + mwa_version, + &metafits_chan_array, + channel_width, + None, + None, + ) + .unwrap(); + + let fine_chan_width_hz: u32 = 20_000; + let num_fine_chans_per_coarse: usize = channel_width as usize / fine_chan_width_hz as usize; + + let calc_fine_chan_centre_array_hz = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &coarse_chan_array, fine_chan_width_hz, num_fine_chans_per_coarse, ); + // Check we have the right number of fine channels + assert_eq!( + calc_fine_chan_centre_array_hz.len(), + 24 * num_fine_chans_per_coarse + ); + + // Check values assert!( approx_eq!( f64, 167_040_000.0, - calc_first_fine_centre_hz, + calc_fine_chan_centre_array_hz[0], F64Margin::default() ), "calculated value: {}", - calc_first_fine_centre_hz + calc_fine_chan_centre_array_hz[0] ); } #[test] -fn test_get_first_fine_chan_centre_hz_mwaxv2_10khz() { - let coarse_chan_width_hz: u32 = 1_280_000; - let rec_channel_num: usize = 131; - let coarse_chan_centre_hz: u32 = rec_channel_num as u32 * coarse_chan_width_hz; - let fine_chan_width_hz: u64 = 10_000; - let num_fine_chans_per_coarse: usize = - coarse_chan_width_hz as usize / fine_chan_width_hz as usize; - - let calc_first_fine_centre_hz: f64 = CoarseChannel::get_first_fine_chan_centre_hz( - MWAVersion::CorrMWAXv2, - coarse_chan_width_hz, - coarse_chan_centre_hz, +fn test_get_fine_chan_centres_array_hz_mwaxv2_10khz() { + let metafits_chan_array: Vec<_> = (131..155).collect(); + let channel_width = 1_280_000; + let mwa_version: MWAVersion = MWAVersion::CorrMWAXv2; + + // Process coarse channels + let coarse_chan_array = CoarseChannel::populate_coarse_channels( + mwa_version, + &metafits_chan_array, + channel_width, + None, + None, + ) + .unwrap(); + + let fine_chan_width_hz: u32 = 10_000; + let num_fine_chans_per_coarse: usize = channel_width as usize / fine_chan_width_hz as usize; + + let calc_fine_chan_centre_array_hz = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &coarse_chan_array, fine_chan_width_hz, num_fine_chans_per_coarse, ); + // Check we have the right number of fine channels + assert_eq!( + calc_fine_chan_centre_array_hz.len(), + 24 * num_fine_chans_per_coarse + ); + + // Check values assert!( approx_eq!( f64, 167_040_000.0, - calc_first_fine_centre_hz, + calc_fine_chan_centre_array_hz[0], F64Margin::default() ), "calculated value: {}", - calc_first_fine_centre_hz + calc_fine_chan_centre_array_hz[0] ); } #[test] -fn test_get_first_fine_chan_centre_hz_mwaxv2_2khz() { - // This provides an odd number of fine channels per coarse - let coarse_chan_width_hz: u32 = 1_280_000; - let rec_channel_num: usize = 131; - let coarse_chan_centre_hz: u32 = rec_channel_num as u32 * coarse_chan_width_hz; - let fine_chan_width_hz: u64 = 256_000; - let num_fine_chans_per_coarse: usize = - coarse_chan_width_hz as usize / fine_chan_width_hz as usize; - - let calc_first_fine_centre_hz: f64 = CoarseChannel::get_first_fine_chan_centre_hz( - MWAVersion::CorrMWAXv2, - coarse_chan_width_hz, - coarse_chan_centre_hz, +fn test_get_fine_chan_centres_array_hz_mwaxv2_2khz() { + let metafits_chan_array: Vec<_> = (131..155).collect(); + let channel_width = 1_280_000; + let mwa_version: MWAVersion = MWAVersion::CorrMWAXv2; + + // Process coarse channels + let coarse_chan_array = CoarseChannel::populate_coarse_channels( + mwa_version, + &metafits_chan_array, + channel_width, + None, + None, + ) + .unwrap(); + + let fine_chan_width_hz: u32 = 256_000; + let num_fine_chans_per_coarse: usize = channel_width as usize / fine_chan_width_hz as usize; + + let calc_fine_chan_centre_array_hz = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &coarse_chan_array, fine_chan_width_hz, num_fine_chans_per_coarse, ); + // Check we have the right number of fine channels + assert_eq!( + calc_fine_chan_centre_array_hz.len(), + 24 * num_fine_chans_per_coarse + ); + + // Check values assert!( approx_eq!( f64, 167_168_000.0, - calc_first_fine_centre_hz, + calc_fine_chan_centre_array_hz[0], F64Margin::default() ), "calculated value: {}", - calc_first_fine_centre_hz + calc_fine_chan_centre_array_hz[0] ); } diff --git a/src/convert/mod.rs b/src/convert/mod.rs index 518e8ea..b55b56c 100644 --- a/src/convert/mod.rs +++ b/src/convert/mod.rs @@ -48,7 +48,6 @@ pub(crate) struct LegacyConversionBaseline { pub yx_conjugate: bool, // if true, we need to conjugate this visibility pub yy_index: usize, // index of where complex xx is in the input buffer pub yy_conjugate: bool, // if true, we need to conjugate this visibility - pub is_cross: bool, // if true, we need to conjugate this visibility AGAIN } impl LegacyConversionBaseline { @@ -81,7 +80,6 @@ impl LegacyConversionBaseline { yx_conjugate: yx < 0, yy_index: yy.abs() as usize, yy_conjugate: yy < 0, - is_cross: ant1 != ant2, } } } diff --git a/src/convert/test.rs b/src/convert/test.rs index 51fbcfa..9578b0f 100644 --- a/src/convert/test.rs +++ b/src/convert/test.rs @@ -49,7 +49,6 @@ fn test_legacy_conversion_baseline_debug() { yx_index: 4, yy_conjugate: false, yy_index: 5, - is_cross: true, }; assert_eq!(format!("{:?}", lcb), "1,0,1,2,-2,3,-3,4,-4,5,-5\n"); diff --git a/src/correlator_context/mod.rs b/src/correlator_context/mod.rs index a65595f..f2e081e 100644 --- a/src/correlator_context/mod.rs +++ b/src/correlator_context/mod.rs @@ -144,12 +144,20 @@ impl CorrelatorContext { )); } // Do gpubox stuff only if we have gpubox files. - let gpubox_info = examine_gpubox_files(&gpubox_filenames, metafits_context.obs_id)?; + let gpubox_info = examine_gpubox_files(gpubox_filenames, metafits_context.obs_id)?; // Populate metafits coarse channels and timesteps now that we know what MWA Version we are dealing with // Populate the coarse channels metafits_context.populate_expected_coarse_channels(gpubox_info.mwa_version)?; + // Now populate the fine channels + metafits_context.metafits_fine_chan_freqs = CoarseChannel::get_fine_chan_centres_array_hz( + gpubox_info.mwa_version, + &metafits_context.metafits_coarse_chans, + metafits_context.corr_fine_chan_width_hz, + metafits_context.num_corr_fine_chans_per_coarse, + ); + // Populate the timesteps metafits_context.populate_expected_timesteps(gpubox_info.mwa_version)?; diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index e198ba3..1a48b7a 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -1025,6 +1025,10 @@ pub struct MetafitsMetadata { pub corr_int_time_ms: u64, /// Number of fine channels in each coarse channel for a correlator observation pub num_corr_fine_chans_per_coarse: usize, + /// Voltage fine_chan_resolution + pub volt_fine_chan_width_hz: u32, + /// Number of fine channels in each coarse channel for a voltage observation + pub num_volt_fine_chans_per_coarse: usize, /// RECVRS Array of receiver numbers (this tells us how many receivers too) pub receivers: *mut usize, /// DELAYS Array of delays @@ -1073,6 +1077,10 @@ pub struct MetafitsMetadata { pub num_metafits_coarse_chans: usize, /// metafits_coarse_chans array pub metafits_coarse_chans: *mut CoarseChannel, + /// Number of fine channels for the whole observation + pub num_metafits_fine_chan_freqs: usize, + /// Vector of fine channel frequencies for the whole observation + pub metafits_fine_chan_freqs: *mut f64, /// Number of timesteps based on the metafits pub num_metafits_timesteps: usize, /// metafits_timesteps array @@ -1330,6 +1338,8 @@ pub unsafe extern "C" fn mwalib_metafits_metadata_get( corr_fine_chan_width_hz, corr_int_time_ms, num_corr_fine_chans_per_coarse, + volt_fine_chan_width_hz, + num_volt_fine_chans_per_coarse, receivers, delays, global_analogue_attenuation_db, @@ -1346,6 +1356,8 @@ pub unsafe extern "C" fn mwalib_metafits_metadata_get( num_visibility_pols, metafits_timesteps: _, // This is populated seperately num_metafits_timesteps, + metafits_fine_chan_freqs, + num_metafits_fine_chan_freqs, metafits_coarse_chans: _, // This is populated seperately num_metafits_coarse_chans, obs_bandwidth_hz, @@ -1387,6 +1399,8 @@ pub unsafe extern "C" fn mwalib_metafits_metadata_get( corr_fine_chan_width_hz: *corr_fine_chan_width_hz, corr_int_time_ms: *corr_int_time_ms, 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: ffi_array_to_boxed_slice(receivers.clone()), delays: ffi_array_to_boxed_slice(delays.clone()), sched_start_utc: sched_start_utc.timestamp(), @@ -1411,6 +1425,8 @@ pub unsafe extern "C" fn mwalib_metafits_metadata_get( num_visibility_pols: *num_visibility_pols, num_metafits_coarse_chans: *num_metafits_coarse_chans, metafits_coarse_chans: ffi_array_to_boxed_slice(coarse_chan_vec), + num_metafits_fine_chan_freqs: *num_metafits_fine_chan_freqs, + metafits_fine_chan_freqs: ffi_array_to_boxed_slice(metafits_fine_chan_freqs.clone()), num_metafits_timesteps: *num_metafits_timesteps, metafits_timesteps: ffi_array_to_boxed_slice(timestep_vec), obs_bandwidth_hz: *obs_bandwidth_hz, @@ -1517,6 +1533,13 @@ pub unsafe extern "C" fn mwalib_metafits_metadata_free( drop(Box::from_raw((*metafits_metadata_ptr).delays)); } + // fine channel freqs + if !(*metafits_metadata_ptr).metafits_fine_chan_freqs.is_null() { + drop(Box::from_raw( + (*metafits_metadata_ptr).metafits_fine_chan_freqs, + )); + } + // Free main metadata struct drop(Box::from_raw(metafits_metadata_ptr)); diff --git a/src/gpubox_files/mod.rs b/src/gpubox_files/mod.rs index cf04505..590f919 100644 --- a/src/gpubox_files/mod.rs +++ b/src/gpubox_files/mod.rs @@ -495,7 +495,7 @@ fn validate_gpubox_metadata_mwa_version( // New correlator files include a version - check that it is present. // For pre v2, ensure the key isn't present let gpu_mwa_version: Option = - get_optional_fits_key!(gpubox_fptr, &gpubox_primary_hdu, "CORR_VER")?; + get_optional_fits_key!(gpubox_fptr, gpubox_primary_hdu, "CORR_VER")?; match mwa_version { MWAVersion::CorrMWAXv2 => match gpu_mwa_version { @@ -758,15 +758,13 @@ pub(crate) fn determine_common_obs_times_and_chans( // Go through all timesteps in the GpuBoxTimeMap. // For each timestep get each coarse channel identifier and add it into the HashSet, then dump them into a vector - let provided_chan_identifiers: Vec = gpubox_time_map + // get the length of the vector - we will use this to test each entry in the GpuboxTimeMap + let max_chans = gpubox_time_map .iter() .flat_map(|ts| ts.1.iter().map(|ch| *ch.0)) .collect::>() .into_iter() - .collect(); - - // get the length of the vector - we will use this to test each entry in the GpuboxTimeMap - let max_chans = provided_chan_identifiers.len(); + .len(); // Filter only the timesteps that have the same coarse channels let mut filtered_timesteps = timemap diff --git a/src/metafits_context/mod.rs b/src/metafits_context/mod.rs index 4837bde..82dcdcd 100644 --- a/src/metafits_context/mod.rs +++ b/src/metafits_context/mod.rs @@ -333,6 +333,10 @@ pub struct MetafitsContext { pub corr_int_time_ms: u64, /// Number of fine channels in each coarse channel for a correlator observation pub num_corr_fine_chans_per_coarse: usize, + /// Voltage fine_chan_resolution + pub volt_fine_chan_width_hz: u32, + /// Number of fine channels in each coarse channel for a voltage observation + pub num_volt_fine_chans_per_coarse: usize, /// RECVRS // Array of receiver numbers (this tells us how many receivers too) pub receivers: Vec, /// DELAYS // Array of delays @@ -363,6 +367,10 @@ pub struct MetafitsContext { pub num_metafits_coarse_chans: usize, /// Vector of coarse channels based on the metafits file pub metafits_coarse_chans: Vec, + /// Number of fine channels for the whole observation + pub num_metafits_fine_chan_freqs: usize, + /// Vector of fine channel frequencies for the whole observation + pub metafits_fine_chan_freqs: Vec, /// Total bandwidth of observation assuming we have all coarse channels pub obs_bandwidth_hz: u32, /// Bandwidth of each coarse channel @@ -399,9 +407,38 @@ impl MetafitsContext { // Call the internal new metafits method let mut new_context = MetafitsContext::new_internal(metafits)?; + // Update the voltage fine channel size now that we know which mwaversion we are using + if mwa_version == MWAVersion::VCSMWAXv2 { + // MWAX VCS- the data is unchannelised so coarse chan width == fine chan width + new_context.volt_fine_chan_width_hz = new_context.coarse_chan_width_hz; + new_context.num_volt_fine_chans_per_coarse = 1; + } + // Populate the coarse channels new_context.populate_expected_coarse_channels(mwa_version)?; + // Now populate the fine channels + new_context.metafits_fine_chan_freqs = CoarseChannel::get_fine_chan_centres_array_hz( + mwa_version, + &new_context.metafits_coarse_chans, + match mwa_version { + MWAVersion::VCSLegacyRecombined | MWAVersion::VCSMWAXv2 => { + new_context.volt_fine_chan_width_hz + } + MWAVersion::CorrLegacy | MWAVersion::CorrOldLegacy | MWAVersion::CorrMWAXv2 => { + new_context.corr_fine_chan_width_hz + } + }, + match mwa_version { + MWAVersion::VCSLegacyRecombined | MWAVersion::VCSMWAXv2 => { + new_context.num_volt_fine_chans_per_coarse + } + MWAVersion::CorrLegacy | MWAVersion::CorrOldLegacy | MWAVersion::CorrMWAXv2 => { + new_context.num_corr_fine_chans_per_coarse + } + }, + ); + // Populate the timesteps new_context.populate_expected_timesteps(mwa_version)?; @@ -624,19 +661,34 @@ impl MetafitsContext { metafits_observation_bandwidth_hz, )?; + // Populate an empty vector for the coarse channels until we know the MWAVersion + // This is because the coarse channel vector will be different depending on the MWAVersion let metafits_coarse_chans: Vec = Vec::with_capacity(metafits_coarse_chan_vec.len()); let num_metafits_coarse_chans: usize = 0; + // Populate fine channel frequencies- we know enough to do this now + let num_metafits_fine_chan_freqs: usize = metafits_coarse_chan_vec.len(); + let metafits_fine_chan_freqs: Vec = Vec::with_capacity(num_metafits_fine_chan_freqs); + // Fine-channel resolution. The FINECHAN value in the metafits is in units // of kHz - make it Hz. - let fine_chan_width_hz: u32 = { + let corr_fine_chan_width_hz: u32 = { let fc: f64 = get_required_fits_key!(&mut metafits_fptr, &metafits_hdu, "FINECHAN")?; (fc * 1000.).round() as _ }; // Determine the number of fine channels per coarse channel. let num_corr_fine_chans_per_coarse = - (metafits_coarse_chan_width_hz / fine_chan_width_hz) as usize; + (metafits_coarse_chan_width_hz / corr_fine_chan_width_hz) as usize; + + // Fine-channel resolution. MWA Legacy is 10 kHz, MWAX is unchannelised + // For now we specify the Legacy VCS, until we know what type of obs this is when we get the + // MWAVersion in the more specialised methods which populate the metafits info. + let volt_fine_chan_width_hz: u32 = 10_000; + + // Determine the number of fine channels per coarse channel. + let num_volt_fine_chans_per_coarse = + (metafits_coarse_chan_width_hz / volt_fine_chan_width_hz) as usize; Ok(MetafitsContext { obs_id: obsid, @@ -675,9 +727,11 @@ impl MetafitsContext { geometric_delays_applied, cable_delays_applied, calibration_delays_and_gains_applied, - corr_fine_chan_width_hz: fine_chan_width_hz, + corr_fine_chan_width_hz, corr_int_time_ms: integration_time_ms, num_corr_fine_chans_per_coarse, + volt_fine_chan_width_hz, + num_volt_fine_chans_per_coarse, receivers, delays, global_analogue_attenuation_db, @@ -689,10 +743,12 @@ impl MetafitsContext { num_rf_inputs, rf_inputs, num_ant_pols: num_antenna_pols, + num_metafits_coarse_chans, metafits_coarse_chans, + num_metafits_fine_chan_freqs, + metafits_fine_chan_freqs, num_metafits_timesteps, metafits_timesteps, - num_metafits_coarse_chans, obs_bandwidth_hz: metafits_observation_bandwidth_hz, coarse_chan_width_hz: metafits_coarse_chan_width_hz, centre_freq_hz, @@ -801,14 +857,18 @@ impl fmt::Display for MetafitsContext { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!( f, - r#"MetafitsContext ( - obsid: {obsid}, - mode: {mode}, + r#"MetafitsContext ( + obsid: {obsid}, + mode: {mode}, + + If Correlator Mode: + fine channel resolution: {fcw} kHz, + integration time: {int_time:.2} s + num fine channels/coarse: {nfcpc}, - Correlator Mode: - fine channel resolution: {fcw} kHz, - integration time: {int_time:.2} s - num fine channels/coarse: {nfcpc}, + If Voltage Mode: + fine channel resolution: {vfcw} kHz, + num fine channels/coarse: {nvfcpc}, Geometric delays applied : {geodel}, Cable length corrections applied : {cabledel}, @@ -833,10 +893,15 @@ impl fmt::Display for MetafitsContext { Quack time: {quack_duration} s, Good UNIX start time: {good_time}, + Num timesteps: {nts}, Timesteps: {ts:?}, + Num coarse channels: {ncc}, Coarse Channels: {cc:?}, + Num fine channels: {nfc}, + Fine Channels (kHz): {fc:?}, + R.A. (tile_pointing): {rtpc} degrees, Dec. (tile_pointing): {dtpc} degrees, R.A. (phase center): {rppc:?} degrees, @@ -888,7 +953,14 @@ impl fmt::Display for MetafitsContext { quack_duration = self.quack_time_duration_ms as f64 / 1e3, good_time = self.good_time_unix_ms as f64 / 1e3, ts = self.metafits_timesteps, + nts = self.metafits_timesteps.len(), cc = self.metafits_coarse_chans, + ncc = self.metafits_coarse_chans.len(), + nfc = self.metafits_fine_chan_freqs.len(), + fc = self + .metafits_fine_chan_freqs + .iter() + .map(|f| format!("{:.3} ", f / 1000.)), rtpc = self.ra_tile_pointing_degrees, dtpc = self.dec_tile_pointing_degrees, rppc = Some(self.ra_phase_center_degrees), @@ -923,6 +995,8 @@ impl fmt::Display for MetafitsContext { geodel = self.geometric_delays_applied, cabledel = self.cable_delays_applied, calibdel = self.calibration_delays_and_gains_applied, + vfcw = self.volt_fine_chan_width_hz as f64 / 1e3, + nvfcpc = self.num_volt_fine_chans_per_coarse, fcw = self.corr_fine_chan_width_hz as f64 / 1e3, nfcpc = self.num_corr_fine_chans_per_coarse, int_time = self.corr_int_time_ms as f64 / 1e3, diff --git a/src/metafits_context/test.rs b/src/metafits_context/test.rs index b51b347..fb66d56 100644 --- a/src/metafits_context/test.rs +++ b/src/metafits_context/test.rs @@ -22,7 +22,28 @@ fn test_metafits_context_new_invalid() { } #[test] -fn test_metafits_context_new_valid() { +fn test_metafits_context_new_vcslegacy_valid() { + // Open the test mwa v 1 metafits file + let metafits_filename = "test_files/1101503312_1_timestep/1101503312.metafits"; + + // + // Read the observation using mwalib + // + // Open a context and load in a test metafits + let context = MetafitsContext::new(&metafits_filename, MWAVersion::VCSLegacyRecombined) + .expect("Failed to create MetafitsContext"); + + // Test the properties of the context object match what we expect + + // obsid: 1101503312, + assert_eq!(context.obs_id, 1_101_503_312); + + assert_eq!(context.volt_fine_chan_width_hz, 10_000); + assert_eq!(context.num_volt_fine_chans_per_coarse, 128); +} + +#[test] +fn test_metafits_context_new_corrlegacy_valid() { // Open the test mwa v 1 metafits file let metafits_filename = "test_files/1101503312_1_timestep/1101503312.metafits"; @@ -202,6 +223,27 @@ fn test_metafits_context_new_valid() { assert_eq!(VisPol::YY.to_string(), "YY"); } +#[test] +fn test_metafits_context_new_vcsmwax2_valid() { + // Open the test mwa v 1 metafits file + let metafits_filename = "test_files/1101503312_1_timestep/1101503312.metafits"; + + // + // Read the observation using mwalib + // + // Open a context and load in a test metafits + let context = MetafitsContext::new(&metafits_filename, MWAVersion::VCSMWAXv2) + .expect("Failed to create MetafitsContext"); + + // Test the properties of the context object match what we expect + + // obsid: 1101503312, + assert_eq!(context.obs_id, 1_101_503_312); + + assert_eq!(context.volt_fine_chan_width_hz, 1_280_000); + assert_eq!(context.num_volt_fine_chans_per_coarse, 1); +} + #[test] fn test_populate_expected_timesteps() { // Note the timesteps returned are fully tested in the timesteps tests, so this is checking the metafits_context calling of that code diff --git a/src/rfinput/test.rs b/src/rfinput/test.rs index a1c6354..d84ab8d 100644 --- a/src/rfinput/test.rs +++ b/src/rfinput/test.rs @@ -119,7 +119,7 @@ fn test_read_metafits_values_from_row_0() { fn test_read_metafits_values_from_invalid_metafits() { let metafits_filename = "read_metafits_values_from_invalid_metafits.metafits"; - with_new_temp_fits_file(&metafits_filename, |metafits_fptr| { + with_new_temp_fits_file(metafits_filename, |metafits_fptr| { // Create a tiledata hdu let first_description = ColumnDescription::new("A") .with_type(ColumnDataType::Int) diff --git a/src/voltage_context/mod.rs b/src/voltage_context/mod.rs index b56257f..227da87 100644 --- a/src/voltage_context/mod.rs +++ b/src/voltage_context/mod.rs @@ -163,12 +163,27 @@ impl VoltageContext { if voltage_filenames.is_empty() { return Err(MwalibError::Voltage(VoltageFileError::NoVoltageFiles)); } - let voltage_info = examine_voltage_files(&metafits_context, &voltage_filenames)?; + let voltage_info = examine_voltage_files(&metafits_context, voltage_filenames)?; + + // Update the voltage fine channel size now that we know which mwaversion we are using + if voltage_info.mwa_version == MWAVersion::VCSMWAXv2 { + // MWAX VCS- the data is unchannelised so coarse chan width == fine chan width + metafits_context.volt_fine_chan_width_hz = metafits_context.coarse_chan_width_hz; + metafits_context.num_volt_fine_chans_per_coarse = 1; + } // Populate metafits coarse channels and timesteps now that we know what MWA Version we are dealing with // Populate the coarse channels metafits_context.populate_expected_coarse_channels(voltage_info.mwa_version)?; + // Now populate the fine channels + metafits_context.metafits_fine_chan_freqs = CoarseChannel::get_fine_chan_centres_array_hz( + voltage_info.mwa_version, + &metafits_context.metafits_coarse_chans, + metafits_context.volt_fine_chan_width_hz, + metafits_context.num_volt_fine_chans_per_coarse, + ); + // Populate the timesteps metafits_context.populate_expected_timesteps(voltage_info.mwa_version)?; @@ -198,23 +213,9 @@ impl VoltageContext { voltage_files::populate_provided_coarse_channels(&voltage_info.time_map, &coarse_chans); let num_provided_coarse_chan_indices = provided_coarse_chan_indices.len(); - // Fine-channel resolution. MWA Legacy is 10 kHz, MWAX is 1.28 MHz (unchannelised) - let fine_chan_width_hz: u32 = match voltage_info.mwa_version { - MWAVersion::VCSLegacyRecombined => 10_000, - MWAVersion::VCSMWAXv2 => { - metafits_context.obs_bandwidth_hz - / metafits_context.num_metafits_coarse_chans as u32 - } - _ => { - return Err(MwalibError::Voltage(VoltageFileError::InvalidMwaVersion { - mwa_version: voltage_info.mwa_version, - })) - } - }; - - // Determine the number of fine channels per coarse channel. - let num_fine_chans_per_coarse = - (metafits_context.coarse_chan_width_hz / fine_chan_width_hz) as usize; + // Fine-channel resolution & number of fine chans per coarse + let fine_chan_width_hz = metafits_context.volt_fine_chan_width_hz; + let num_fine_chans_per_coarse = metafits_context.num_volt_fine_chans_per_coarse; let coarse_chan_width_hz = metafits_context.coarse_chan_width_hz; @@ -576,11 +577,8 @@ impl VoltageContext { let channel_identifier = self.coarse_chans[coarse_chan_index].gpubox_number; // Validate the gpstime - let gps_second_end = VoltageContext::validate_gps_time_parameters( - &self, - gps_second_start, - gps_second_count, - )?; + let gps_second_end = + VoltageContext::validate_gps_time_parameters(self, gps_second_start, gps_second_count)?; // Determine which timestep(s) we need to cover the start and end gps times. // NOTE: mwax has 8 gps seconds per timestep, legacy vcs has 1 diff --git a/src/voltage_context/test.rs b/src/voltage_context/test.rs index ad78a16..f6cfbdf 100644 --- a/src/voltage_context/test.rs +++ b/src/voltage_context/test.rs @@ -240,7 +240,7 @@ pub(crate) fn get_test_voltage_files(mwa_version: MWAVersion) -> Vec { VCS_MWAXV2_TEST_DATA_CREATED.call_once(|| { // Create this test data, but only once! for (i, f) in test_filenames.iter().enumerate() { - generate_test_voltage_file_mwax(&f, i as u8).unwrap(); + generate_test_voltage_file_mwax(f, i as u8).unwrap(); } }); } @@ -255,7 +255,7 @@ pub(crate) fn get_test_voltage_files(mwa_version: MWAVersion) -> Vec { // This ensure the test data is created once only VCS_LEGACY_TEST_DATA_CREATED.call_once(|| { for (i, f) in test_filenames.iter().enumerate() { - generate_test_voltage_file_legacy_recombined(&f, i as u8).unwrap(); + generate_test_voltage_file_legacy_recombined(f, i as u8).unwrap(); } }); } diff --git a/src/voltage_files/mod.rs b/src/voltage_files/mod.rs index 58e3a06..4be2ee2 100644 --- a/src/voltage_files/mod.rs +++ b/src/voltage_files/mod.rs @@ -602,15 +602,13 @@ pub(crate) fn determine_common_obs_times_and_chans( // Go through all timesteps in the GpuBoxTimeMap. // For each timestep get each coarse channel identifier and add it into the HashSet, then dump them into a vector - let provided_chan_identifiers: Vec = voltage_time_map + // get the length of the vector - we will use this to test each entry in the VoltageTimeMap + let max_chans = voltage_time_map .iter() .flat_map(|ts| ts.1.iter().map(|ch| *ch.0)) .collect::>() .into_iter() - .collect(); - - // get the length of the vector - we will use this to test each entry in the VoltageTimeMap - let max_chans = provided_chan_identifiers.len(); + .len(); // Filter only the timesteps that have the same coarse channels let mut filtered_timesteps = timemap