use crate::utils::str_to_f64;
use crate::data_pairs::DecibelAngle;
use crate::data_pairs::DecibelAngleMatrix;
use crate::data_pairs::MagnitudeAngle;
use crate::data_pairs::MagnitudeAngleMatrix;
use crate::data_pairs::RealImaginary;
use crate::data_pairs::RealImaginaryMatrix;
#[derive(Clone, Debug)]
pub struct ParsedDataLine {
pub frequency: f64,
pub s_ri: RealImaginaryMatrix,
pub s_db: DecibelAngleMatrix,
pub s_ma: MagnitudeAngleMatrix,
}
pub(crate) fn parse_data_line(
data_lines: Vec<String>,
format: &String,
n: &i32,
frequency_unit: &String,
) -> ParsedDataLine {
let expect_number_of_parts = 1 + (2 * (n * n));
let mut parts = Vec::new();
for line in &data_lines {
let line_parts: Vec<_> = line
.split_whitespace()
.filter(|s| !s.starts_with('!')) .collect();
parts.extend(line_parts);
}
let len_parts = parts.len();
if len_parts != expect_number_of_parts as usize {
panic!(
"Data line has unexpected number of parts. Expected {}, got {}",
expect_number_of_parts, len_parts
);
}
let f64_parts: Vec<_> = parts.clone().into_iter().map(str_to_f64).collect();
let n_usize = *n as usize;
let mut frequency = str_to_f64(parts[0]);
if frequency_unit == "THz" {
frequency = rfconversions::frequency::thz_to_hz(frequency);
tracing::trace!("Converted frequency: {} Hz", frequency);
} else if frequency_unit == "GHz" {
frequency = rfconversions::frequency::ghz_to_hz(frequency);
} else if frequency_unit == "MHz" {
frequency = rfconversions::frequency::mhz_to_hz(frequency);
} else if frequency_unit == "kHz" {
frequency = rfconversions::frequency::khz_to_hz(frequency);
} else if frequency_unit == "Hz" {
} else {
panic!("Unsupported frequency unit: {}", frequency_unit);
}
if format == "RI" {
let mut s_ri_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
let idx = 1 + 2 * (row * n_usize + col);
row_vec.push(RealImaginary(f64_parts[idx], f64_parts[idx + 1]));
}
s_ri_data.push(row_vec);
}
let s_ri = RealImaginaryMatrix::from_vec(s_ri_data);
let mut s_db_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
row_vec.push(DecibelAngle::from_real_imaginary(
s_ri.get(row + 1, col + 1),
));
}
s_db_data.push(row_vec);
}
let s_db = DecibelAngleMatrix::from_vec(s_db_data);
let mut s_ma_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
row_vec.push(MagnitudeAngle::from_real_imaginary(
s_ri.get(row + 1, col + 1),
));
}
s_ma_data.push(row_vec);
}
let s_ma = MagnitudeAngleMatrix::from_vec(s_ma_data);
ParsedDataLine {
frequency,
s_ri,
s_db,
s_ma,
}
} else if format == "MA" {
let mut s_ma_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
let idx = 1 + 2 * (row * n_usize + col);
row_vec.push(MagnitudeAngle(f64_parts[idx], f64_parts[idx + 1]));
}
s_ma_data.push(row_vec);
}
let s_ma = MagnitudeAngleMatrix::from_vec(s_ma_data);
let mut s_ri_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
row_vec.push(RealImaginary::from_magnitude_angle(
s_ma.get(row + 1, col + 1),
));
}
s_ri_data.push(row_vec);
}
let s_ri = RealImaginaryMatrix::from_vec(s_ri_data);
let mut s_db_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
row_vec.push(DecibelAngle::from_magnitude_angle(
s_ma.get(row + 1, col + 1),
));
}
s_db_data.push(row_vec);
}
let s_db = DecibelAngleMatrix::from_vec(s_db_data);
ParsedDataLine {
frequency,
s_ri,
s_db,
s_ma,
}
} else if format == "DB" {
let mut s_db_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
let idx = 1 + 2 * (row * n_usize + col);
row_vec.push(DecibelAngle(f64_parts[idx], f64_parts[idx + 1]));
}
s_db_data.push(row_vec);
}
let s_db = DecibelAngleMatrix::from_vec(s_db_data);
let mut s_ri_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
row_vec.push(RealImaginary::from_decibel_angle(
s_db.get(row + 1, col + 1),
));
}
s_ri_data.push(row_vec);
}
let s_ri = RealImaginaryMatrix::from_vec(s_ri_data);
let mut s_ma_data = Vec::new();
for row in 0..n_usize {
let mut row_vec = Vec::new();
for col in 0..n_usize {
row_vec.push(MagnitudeAngle::from_decibel_angle(
s_db.get(row + 1, col + 1),
));
}
s_ma_data.push(row_vec);
}
let s_ma = MagnitudeAngleMatrix::from_vec(s_ma_data);
ParsedDataLine {
frequency,
s_ri,
s_db,
s_ma,
}
} else {
panic!("Unsupported format: {}", format);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn approx_eq(a: f64, b: f64, tol: f64) -> bool {
(a - b).abs() < tol
}
#[test]
fn test_parse_1port_ri_hz() {
let lines = vec!["1000000000 0.5 -0.3".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &1, &"Hz".to_string());
assert!(approx_eq(result.frequency, 1e9, 1.0));
let ri = result.s_ri.get(1, 1);
assert!(approx_eq(ri.0, 0.5, 1e-10));
assert!(approx_eq(ri.1, -0.3, 1e-10));
}
#[test]
fn test_parse_frequency_ghz() {
let lines = vec!["2.4 0.1 0.2".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &1, &"GHz".to_string());
assert!(approx_eq(result.frequency, 2.4e9, 1.0));
}
#[test]
fn test_parse_frequency_mhz() {
let lines = vec!["900 0.1 0.2".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &1, &"MHz".to_string());
assert!(approx_eq(result.frequency, 900e6, 1.0));
}
#[test]
fn test_parse_frequency_khz() {
let lines = vec!["500 0.1 0.2".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &1, &"kHz".to_string());
assert!(approx_eq(result.frequency, 500e3, 1.0));
}
#[test]
fn test_parse_frequency_thz() {
let lines = vec!["0.3 0.1 0.2".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &1, &"THz".to_string());
assert!(approx_eq(result.frequency, 0.3e12, 1.0));
}
#[test]
fn test_parse_1port_ma() {
let lines = vec!["1000000000 0.8 -45.0".to_string()];
let result = parse_data_line(lines, &"MA".to_string(), &1, &"Hz".to_string());
let ma = result.s_ma.get(1, 1);
assert!(approx_eq(ma.0, 0.8, 1e-10));
assert!(approx_eq(ma.1, -45.0, 1e-10));
let ri = result.s_ri.get(1, 1);
let expected_real = 0.8 * (-45.0_f64.to_radians()).cos();
let expected_imag = 0.8 * (-45.0_f64.to_radians()).sin();
assert!(approx_eq(ri.0, expected_real, 1e-6));
assert!(approx_eq(ri.1, expected_imag, 1e-6));
}
#[test]
fn test_parse_1port_db() {
let lines = vec!["1000000000 -3.0 90.0".to_string()];
let result = parse_data_line(lines, &"DB".to_string(), &1, &"Hz".to_string());
let db = result.s_db.get(1, 1);
assert!(approx_eq(db.0, -3.0, 1e-10));
assert!(approx_eq(db.1, 90.0, 1e-10));
let ma = result.s_ma.get(1, 1);
let expected_mag = 10.0_f64.powf(-3.0 / 20.0);
assert!(approx_eq(ma.0, expected_mag, 1e-6));
}
#[test]
fn test_parse_2port_ri() {
let lines = vec!["1e9 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &2, &"Hz".to_string());
let s11 = result.s_ri.get(1, 1);
assert!(approx_eq(s11.0, 0.1, 1e-10));
assert!(approx_eq(s11.1, 0.2, 1e-10));
let s12 = result.s_ri.get(1, 2);
assert!(approx_eq(s12.0, 0.3, 1e-10));
assert!(approx_eq(s12.1, 0.4, 1e-10));
let s21 = result.s_ri.get(2, 1);
assert!(approx_eq(s21.0, 0.5, 1e-10));
assert!(approx_eq(s21.1, 0.6, 1e-10));
let s22 = result.s_ri.get(2, 2);
assert!(approx_eq(s22.0, 0.7, 1e-10));
assert!(approx_eq(s22.1, 0.8, 1e-10));
}
#[test]
fn test_parse_2port_multiline() {
let lines = vec![
"1e9 0.1 0.2 0.3 0.4".to_string(),
"0.5 0.6 0.7 0.8".to_string(),
];
let result = parse_data_line(lines, &"RI".to_string(), &2, &"Hz".to_string());
let s22 = result.s_ri.get(2, 2);
assert!(approx_eq(s22.0, 0.7, 1e-10));
assert!(approx_eq(s22.1, 0.8, 1e-10));
}
#[test]
fn test_parse_inline_comment_single_word_filtered() {
let lines = vec!["1e9 0.5 -0.3 !comment".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &1, &"Hz".to_string());
let ri = result.s_ri.get(1, 1);
assert!(approx_eq(ri.0, 0.5, 1e-10));
assert!(approx_eq(ri.1, -0.3, 1e-10));
}
#[test]
fn test_ri_round_trip_consistency() {
let lines = vec!["1e9 0.6 -0.4".to_string()];
let result = parse_data_line(lines, &"RI".to_string(), &1, &"Hz".to_string());
let ri = result.s_ri.get(1, 1);
let ma = result.s_ma.get(1, 1);
let db = result.s_db.get(1, 1);
let mag = (ri.0 * ri.0 + ri.1 * ri.1).sqrt();
assert!(approx_eq(ma.0, mag, 1e-10));
assert!(approx_eq(db.0, 20.0 * mag.log10(), 1e-6));
}
#[test]
#[should_panic(expected = "Unsupported format")]
fn test_parse_unsupported_format_panics() {
let lines = vec!["1e9 0.1 0.2".to_string()];
parse_data_line(lines, &"XX".to_string(), &1, &"Hz".to_string());
}
#[test]
#[should_panic(expected = "Unsupported frequency unit")]
fn test_parse_unsupported_frequency_unit_panics() {
let lines = vec!["1e9 0.1 0.2".to_string()];
parse_data_line(lines, &"RI".to_string(), &1, &"PHz".to_string());
}
#[test]
#[should_panic(expected = "unexpected number of parts")]
fn test_parse_wrong_number_of_parts_panics() {
let lines = vec!["1e9 0.1".to_string()]; parse_data_line(lines, &"RI".to_string(), &1, &"Hz".to_string());
}
}