use touchstone::Network;
#[test]
fn parse_s1p_hfss_oneport() {
let ntwk = Network::new("files/hfss_oneport.s1p".to_string());
assert_eq!(ntwk.rank, 1);
assert!(!ntwk.f.is_empty());
assert_eq!(ntwk.frequency_unit, "GHz");
assert_eq!(ntwk.format, "MA");
}
#[test]
fn parse_s1p_powerwave() {
let ntwk = Network::new("files/hfss_oneport_powerwave.s1p".to_string());
assert_eq!(ntwk.rank, 1);
assert!(!ntwk.f.is_empty());
}
#[test]
fn s1p_s_parameters_all_formats() {
let ntwk = Network::new("files/hfss_oneport.s1p".to_string());
let s11_db = ntwk.s_db(1, 1);
let s11_ri = ntwk.s_ri(1, 1);
let s11_ma = ntwk.s_ma(1, 1);
assert_eq!(s11_db.len(), ntwk.f.len());
assert_eq!(s11_ri.len(), ntwk.f.len());
assert_eq!(s11_ma.len(), ntwk.f.len());
for i in 0..s11_db.len() {
assert!(s11_db[i].s_db.decibel().is_finite());
assert!(s11_ri[i].s_ri.real().is_finite());
assert!(s11_ma[i].s_ma.magnitude().is_finite());
}
}
#[test]
fn parse_s4p_agilent() {
let ntwk = Network::new("files/Agilent_E5071B.s4p".to_string());
assert_eq!(ntwk.rank, 4);
assert!(!ntwk.f.is_empty());
assert_eq!(ntwk.frequency_unit, "Hz");
assert_eq!(ntwk.format, "DB");
assert!((ntwk.z0 - 75.0).abs() < 0.01);
}
#[test]
fn parse_s4p_rs_znb8() {
let ntwk = Network::new("files/RS_ZNB8.s4p".to_string());
assert_eq!(ntwk.rank, 4);
assert!(!ntwk.f.is_empty());
}
#[test]
fn s4p_all_port_combinations() {
let ntwk = Network::new("files/Agilent_E5071B.s4p".to_string());
for j in 1..=4 {
for k in 1..=4 {
let s = ntwk.s_db(j, k);
assert_eq!(s.len(), ntwk.f.len(), "S{}{} length mismatch", j, k);
for point in &s {
assert!(point.s_db.decibel().is_finite(), "S{}{} has non-finite dB", j, k);
assert!(point.s_db.angle().is_finite(), "S{}{} has non-finite angle", j, k);
}
}
}
}
#[test]
fn option_line_ghz_ma() {
let ntwk = Network::new("files/hfss_oneport.s1p".to_string());
assert_eq!(ntwk.frequency_unit, "GHz");
assert_eq!(ntwk.format, "MA");
assert!((ntwk.z0 - 50.0).abs() < 0.01);
}
#[test]
fn option_line_ghz_db() {
let ntwk = Network::new("files/hfss_threeport_DB.s3p".to_string());
assert_eq!(ntwk.frequency_unit, "GHz");
assert_eq!(ntwk.format, "DB");
}
#[test]
fn option_line_hz_db_75ohm() {
let ntwk = Network::new("files/Agilent_E5071B.s4p".to_string());
assert_eq!(ntwk.frequency_unit, "Hz");
assert_eq!(ntwk.format, "DB");
assert!((ntwk.z0 - 75.0).abs() < 0.01);
}
#[test]
fn option_line_50ohm_threeport_variants() {
let db50 = Network::new("files/hfss_threeport_DB_50Ohm.s3p".to_string());
let ma50 = Network::new("files/hfss_threeport_MA_50Ohm.s3p".to_string());
assert!((db50.z0 - 50.0).abs() < 0.01);
assert!((ma50.z0 - 50.0).abs() < 0.01);
}
#[test]
fn cascade_produces_valid_data() {
let net1 = Network::new("files/ntwk1.s2p".to_string());
let net2 = Network::new("files/ntwk2.s2p".to_string());
let cascaded = net1.cascade(&net2);
assert_eq!(cascaded.rank, 2);
assert_eq!(cascaded.f.len(), net1.f.len());
for (j, k) in [(1, 1), (1, 2), (2, 1), (2, 2)] {
for point in cascaded.s_db(j, k) {
assert!(point.s_db.decibel().is_finite());
}
}
}
#[test]
fn cascade_matches_reference_file() {
let net1 = Network::new("files/ntwk1.s2p".to_string());
let net2 = Network::new("files/ntwk2.s2p".to_string());
let cascaded = net1.cascade(&net2);
let reference = Network::new("files/cascade_ntwk1_ntwk2.s2p".to_string());
assert_eq!(cascaded.f.len(), reference.f.len());
let cas_s21 = cascaded.s_db(2, 1);
let ref_s21 = reference.s_db(2, 1);
for i in 0..cas_s21.len() {
let diff = (cas_s21[i].s_db.decibel() - ref_s21[i].s_db.decibel()).abs();
assert!(diff < 0.5, "S21 dB mismatch at index {}: {} vs {} (diff {})",
i, cas_s21[i].s_db.decibel(), ref_s21[i].s_db.decibel(), diff);
}
}
#[test]
fn mul_trait_cascade() {
let net1 = Network::new("files/ntwk1.s2p".to_string());
let expected_len = net1.f.len();
let net2 = Network::new("files/ntwk2.s2p".to_string());
let cascaded = net1 * net2;
assert_eq!(cascaded.rank, 2);
assert_eq!(cascaded.f.len(), expected_len);
}
#[test]
fn s_db_ri_ma_consistency() {
let ntwk = Network::new("files/ntwk1.s2p".to_string());
let s11_db = ntwk.s_db(1, 1);
let s11_ri = ntwk.s_ri(1, 1);
let s11_ma = ntwk.s_ma(1, 1);
for i in 0..s11_db.len() {
let mag_from_ma = s11_ma[i].s_ma.magnitude();
let mag_from_ri = s11_ri[i].s_ri.magnitude();
let mag_from_db = s11_db[i].s_db.magnitude();
assert!((mag_from_ma - mag_from_ri).abs() < 1e-6,
"MA vs RI magnitude mismatch at {}: {} vs {}", i, mag_from_ma, mag_from_ri);
assert!((mag_from_ma - mag_from_db).abs() < 1e-6,
"MA vs DB magnitude mismatch at {}: {} vs {}", i, mag_from_ma, mag_from_db);
let angle_from_ma = s11_ma[i].s_ma.angle();
let angle_from_ri = s11_ri[i].s_ri.angle();
let angle_from_db = s11_db[i].s_db.angle();
assert!((angle_from_ma - angle_from_ri).abs() < 1e-4,
"MA vs RI angle mismatch at {}: {} vs {}", i, angle_from_ma, angle_from_ri);
assert!((angle_from_ma - angle_from_db).abs() < 1e-4,
"MA vs DB angle mismatch at {}: {} vs {}", i, angle_from_ma, angle_from_db);
}
}
#[test]
#[should_panic]
fn nonexistent_file_panics() {
Network::new("files/does_not_exist.s2p".to_string());
}
#[test]
#[should_panic]
fn invalid_extension_panics() {
Network::new("files/ntwk1.txt".to_string());
}
#[test]
fn round_trip_s2p() {
let original = Network::new("files/ntwk1.s2p".to_string());
let tmp = "files/test_round_trip_edge.s2p";
original.save(tmp).unwrap();
let reloaded = Network::new(tmp.to_string());
assert_eq!(original.rank, reloaded.rank);
assert_eq!(original.f.len(), reloaded.f.len());
assert!((original.z0 - reloaded.z0).abs() < 0.01);
for i in 0..original.f.len() {
let freq_diff = (original.f[i] - reloaded.f[i]).abs();
assert!(freq_diff < 1e-3, "Frequency mismatch at {}: {} vs {}", i, original.f[i], reloaded.f[i]);
}
for (j, k) in [(1, 1), (1, 2), (2, 1), (2, 2)] {
let orig_s = original.s_ri(j, k);
let reload_s = reloaded.s_ri(j, k);
for i in 0..orig_s.len() {
let real_diff = (orig_s[i].s_ri.real() - reload_s[i].s_ri.real()).abs();
let imag_diff = (orig_s[i].s_ri.imaginary() - reload_s[i].s_ri.imaginary()).abs();
assert!(real_diff < 1e-4, "S{}{} real mismatch at {}", j, k, i);
assert!(imag_diff < 1e-4, "S{}{} imag mismatch at {}", j, k, i);
}
}
std::fs::remove_file(tmp).unwrap();
}
#[test]
fn round_trip_s3p() {
let original = Network::new("files/hfss_threeport_DB.s3p".to_string());
let tmp = "files/test_round_trip_s3p.s3p";
original.save(tmp).unwrap();
let reloaded = Network::new(tmp.to_string());
assert_eq!(original.rank, reloaded.rank);
assert_eq!(original.f.len(), reloaded.f.len());
let orig_s11 = original.s_db(1, 1);
let reload_s11 = reloaded.s_db(1, 1);
for i in 0..orig_s11.len() {
let db_diff = (orig_s11[i].s_db.decibel() - reload_s11[i].s_db.decibel()).abs();
assert!(db_diff < 0.1, "S11 dB round-trip mismatch at {}: {} vs {}",
i, orig_s11[i].s_db.decibel(), reload_s11[i].s_db.decibel());
}
std::fs::remove_file(tmp).unwrap();
}
#[test]
fn round_trip_preserves_cascade_result() {
let net1 = Network::new("files/ntwk1.s2p".to_string());
let net2 = Network::new("files/ntwk2.s2p".to_string());
let cascaded = net1.cascade(&net2);
let tmp = "files/test_round_trip_cascade.s2p";
cascaded.save(tmp).unwrap();
let reloaded = Network::new(tmp.to_string());
assert_eq!(cascaded.f.len(), reloaded.f.len());
let cas_s21 = cascaded.s_ri(2, 1);
let rel_s21 = reloaded.s_ri(2, 1);
for i in 0..cas_s21.len() {
let real_diff = (cas_s21[i].s_ri.real() - rel_s21[i].s_ri.real()).abs();
assert!(real_diff < 1e-4, "Cascade round-trip real mismatch at {}", i);
}
std::fs::remove_file(tmp).unwrap();
}
#[test]
fn parse_s32p() {
let ntwk = Network::new("files/ntwk.s32p".to_string());
assert_eq!(ntwk.rank, 32);
assert!(!ntwk.f.is_empty());
}
#[test]
fn parse_s8p() {
let ntwk = Network::new("files/hfss_19.2.s8p".to_string());
assert_eq!(ntwk.rank, 8);
assert!(!ntwk.f.is_empty());
}
#[test]
fn parse_s10p() {
let ntwk = Network::new("files/hfss_19.2.s10p".to_string());
assert_eq!(ntwk.rank, 10);
assert!(!ntwk.f.is_empty());
}
#[test]
fn parse_all_threeport_variants() {
let files = [
"files/hfss_threeport_DB.s3p",
"files/hfss_threeport_DB_50Ohm.s3p",
"files/hfss_threeport_MA.s3p",
"files/hfss_threeport_MA_50Ohm.s3p",
"files/hfss_threeport_MA_without_gamma_z0_50Ohm.s3p",
];
for file in files {
let ntwk = Network::new(file.to_string());
assert_eq!(ntwk.rank, 3, "Wrong rank for {}", file);
assert!(!ntwk.f.is_empty(), "No frequencies for {}", file);
}
}