use crate::antenna;
use crate::MWAVersion;
use std::fmt::Display;
use std::fs::File;
use std::io::{Error, Write};
use std::{fmt::Debug, mem, slice};
#[cfg(test)]
pub(crate) mod test;
pub fn dms_to_degrees(degrees: i32, minutes: u32, seconds: f64) -> f64 {
let deg = degrees.abs() as f64 + (minutes as f64 / 60_f64) + (seconds.abs() / 3600_f64);
if degrees < 0 {
-deg
} else {
deg
}
}
pub fn as_u8_slice(v: &[f32]) -> &[u8] {
let element_size = mem::size_of::<i32>();
unsafe { slice::from_raw_parts(v.as_ptr() as *const u8, v.len() * element_size) }
}
pub fn get_baseline_count(antennas: usize) -> usize {
antennas * (antennas + 1) / 2
}
pub fn get_antennas_from_baseline(baseline: usize, num_antennas: usize) -> Option<(usize, usize)> {
let ant1 = (-0.5
* ((4 * num_antennas * num_antennas + 4 * num_antennas - 8 * baseline + 1) as f32).sqrt()
+ num_antennas as f32
+ 1. / 2.) as usize;
let ant2 = baseline - (ant1 * num_antennas - (ant1 * ant1 + ant1) / 2);
if ant1 > num_antennas - 1 || ant2 > num_antennas - 1 {
None
} else {
Some((ant1, ant2))
}
}
pub fn get_baseline_from_antennas(
antenna1: usize,
antenna2: usize,
num_antennas: usize,
) -> Option<usize> {
let mut baseline_index = 0;
for ant1 in 0..num_antennas {
for ant2 in ant1..num_antennas {
if ant1 == antenna1 && ant2 == antenna2 {
return Some(baseline_index);
}
baseline_index += 1;
}
}
None
}
pub fn get_baseline_from_antenna_names(
antenna1_tile_name: String,
antenna2_tile_name: String,
antennas: &[antenna::Antenna],
) -> usize {
let mut baseline_index = 0;
let antenna1_index = antennas
.iter()
.position(|a| a.tile_name == antenna1_tile_name)
.unwrap();
let antenna2_index = antennas
.iter()
.position(|a| a.tile_name == antenna2_tile_name)
.unwrap();
for ant1 in 0..antennas.len() {
for ant2 in ant1..antennas.len() {
if ant1 == antenna1_index && ant2 == antenna2_index {
return baseline_index;
}
baseline_index += 1;
}
}
unreachable!("Baseline was not found")
}
pub fn convert_gpstime_to_unixtime(
gpstime_ms: u64,
mwa_start_gpstime_ms: u64,
mwa_start_unixtime_ms: u64,
) -> u64 {
let offset_ms = mwa_start_unixtime_ms - mwa_start_gpstime_ms;
gpstime_ms + offset_ms
}
pub fn convert_unixtime_to_gpstime(
unixtime_ms: u64,
mwa_start_gpstime_ms: u64,
mwa_start_unixtime_ms: u64,
) -> u64 {
match unixtime_ms {
0 => 0,
_ => {
let offset_ms = mwa_start_unixtime_ms - mwa_start_gpstime_ms;
unixtime_ms - offset_ms
}
}
}
pub fn has_whitening_filter(flavour: &str, whitening_filter: i32) -> bool {
if whitening_filter == -1 {
if flavour.len() >= 3 {
match flavour[0..3].to_uppercase().as_str() {
"RG6" => !matches!(flavour, "RG6_90"),
"LMR" => true,
_ => false,
}
} else {
false
}
} else {
whitening_filter != 0
}
}
pub fn eq_with_nan_eq_f32(a: f32, b: f32) -> bool {
(a.is_nan() && b.is_nan()) || (a == b)
}
pub fn vec_compare_f32(va: &[f32], vb: &[f32]) -> bool {
(va.len() == vb.len()) && va.iter()
.zip(vb)
.all(|(a,b)| eq_with_nan_eq_f32(*a,*b))
}
pub fn eq_with_nan_eq_f64(a: f64, b: f64) -> bool {
(a.is_nan() && b.is_nan()) || (a == b)
}
pub fn vec_compare_f64(va: &[f64], vb: &[f64]) -> bool {
(va.len() == vb.len()) && va.iter()
.zip(vb)
.all(|(a,b)| eq_with_nan_eq_f64(*a,*b))
}
pub fn pretty_print_vec<T>(vec: &[T], num_elements: usize) -> String
where
T: Debug + Display,
{
let vec_len = vec.len();
let return_str: String;
if vec_len == 0 || num_elements == 0 {
return String::from("[]");
}
if vec_len <= (num_elements * 2) {
let full_str = vec
.iter()
.fold(String::new(), |acc, num| {
acc + format!("{}", &num).as_str() + ","
})
.to_string();
return_str = format!("[{}]", full_str.strip_suffix(",").unwrap_or(&full_str));
} else {
let start_str = vec[0..num_elements]
.iter()
.fold(String::new(), |acc, num| {
acc + format!("{}", &num).as_str() + ","
})
.to_string();
let end_str = vec[vec_len - num_elements..]
.iter()
.fold(String::new(), |acc, num| {
acc + format!("{}", &num).as_str() + ","
})
.to_string();
return_str = format!(
"[{}...{}]",
start_str.strip_suffix(",").unwrap_or(&start_str),
end_str.strip_suffix(",").unwrap_or(&end_str),
);
};
return return_str;
}
pub fn pretty_print_opt_vec<T>(opt_vec: &Option<Vec<T>>, num_elements: usize) -> String
where
T: Debug + Display,
{
match opt_vec {
Some(v) => pretty_print_vec(v, num_elements),
None => String::from("[]"),
}
}
#[allow(clippy::too_many_arguments)]
pub fn generate_test_voltage_file(
filename: &str,
mwa_version: MWAVersion,
num_voltage_blocks: usize,
samples_per_block: usize,
rf_inputs: usize,
fine_chans: usize,
bytes_per_sample: usize,
initial_value: u8,
) -> Result<String, Error> {
let mut output_file: File = File::create(filename)?;
if mwa_version == MWAVersion::VCSMWAXv2 {
let header_buffer: Vec<u8> = vec![0x01; 4096];
output_file
.write_all(&header_buffer)
.expect("Cannot write header!");
}
let num_bytes_per_voltage_block = samples_per_block * rf_inputs * fine_chans * bytes_per_sample;
if mwa_version == MWAVersion::VCSMWAXv2 {
let delay_buffer: Vec<u8> = vec![0x02; num_bytes_per_voltage_block];
output_file
.write_all(&delay_buffer)
.expect("Cannot write delay block!");
}
for b in 0..num_voltage_blocks {
let mut value1: u8;
let mut value2: u8;
let mut voltage_block_buffer: Vec<u8> = vec![0; num_bytes_per_voltage_block];
let mut bptr: usize = 0;
match mwa_version {
MWAVersion::VCSMWAXv2 => {
for r in 0..rf_inputs {
for s in 0..samples_per_block {
value1 =
((initial_value as u64 + (b * 5 + r * 4 + s * 2) as u64) % 256) as u8;
value2 = 255 - value1;
voltage_block_buffer[bptr] = value1;
bptr += 1;
voltage_block_buffer[bptr] = value2;
bptr += 1;
}
}
}
MWAVersion::VCSLegacyRecombined => {
for s in 0..samples_per_block {
for f in 0..fine_chans {
for r in 0..rf_inputs {
value1 = ((initial_value as u64 + (s * 4 + f * 3 + r * 2) as u64) % 256)
as u8;
voltage_block_buffer[bptr] = value1;
bptr += 1;
}
}
}
}
_ => {}
}
output_file
.write_all(&voltage_block_buffer)
.expect("Cannot write voltage data block");
}
output_file.flush()?;
Ok(String::from(filename))
}