#[cfg(test)]
mod tests {
use crate::constellationlib::ConstellationMap;
use crate::pybridge::bridge::PyRustBridge;
use crate::pybridge::helpers::PythonResult;
use crate::time::Timescale;
fn parse_string(result: &str) -> String {
let parsed = PythonResult::try_from(result).expect("Failed to parse Python result");
match parsed {
PythonResult::String(s) => s,
_ => panic!("Expected String result, got {:?}", parsed),
}
}
#[test]
fn test_known_stars_vs_skyfield() {
let bridge = PyRustBridge::new().expect("Failed to create Python bridge");
let ts = Timescale::default();
let map = ConstellationMap::new();
let test_stars = [
("Polaris", 2.5302, 89.2641), ("Sirius", 6.7525, -16.7161), ("Betelgeuse", 5.9195, 7.4071), ("Vega", 18.6156, 38.7837), ("Rigel", 5.2423, -8.2016), ("Antares", 16.4901, -26.4320), ("Canopus", 6.3992, -52.6957), ("Deneb", 20.6905, 45.2803), ];
for (name, ra_hours, dec_deg) in &test_stars {
let py_result = bridge
.run_py_to_json(&format!(
r#"
from skyfield.api import load_constellation_map, position_of_radec
constellation_at = load_constellation_map()
pos = position_of_radec({ra_hours}, {dec_deg})
rust.collect_string(constellation_at(pos))
"#
))
.unwrap_or_else(|e| panic!("Python failed for {name}: {e}"));
let py_constellation = parse_string(&py_result);
let pos = crate::positions::Position::barycentric(
direction_from_ra_dec(*ra_hours, *dec_deg),
nalgebra::Vector3::zeros(),
0,
);
let rust_constellation = map.constellation_of(&pos, &ts);
assert_eq!(
rust_constellation, py_constellation,
"{name}: rust={rust_constellation} python={py_constellation}"
);
}
}
#[test]
fn test_sky_grid_vs_skyfield() {
let bridge = PyRustBridge::new().expect("Failed to create Python bridge");
let ts = Timescale::default();
let map = ConstellationMap::new();
let ra_values = [0.0, 3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0];
let dec_values = [-60.0, -30.0, 0.0, 30.0, 60.0];
for &ra in &ra_values {
for &dec in &dec_values {
let py_result = bridge
.run_py_to_json(&format!(
r#"
from skyfield.api import load_constellation_map, position_of_radec
constellation_at = load_constellation_map()
pos = position_of_radec({ra}, {dec})
rust.collect_string(constellation_at(pos))
"#
))
.unwrap_or_else(|e| panic!("Python failed for RA={ra} Dec={dec}: {e}"));
let py_constellation = parse_string(&py_result);
let pos = crate::positions::Position::barycentric(
direction_from_ra_dec(ra, dec),
nalgebra::Vector3::zeros(),
0,
);
let rust_constellation = map.constellation_of(&pos, &ts);
assert_eq!(
rust_constellation, py_constellation,
"RA={ra}h Dec={dec}°: rust={rust_constellation} python={py_constellation}"
);
}
}
}
#[test]
fn test_planet_constellations_vs_skyfield() {
let bridge = PyRustBridge::new().expect("Failed to create Python bridge");
let ts = Timescale::default();
let map = ConstellationMap::new();
let py_result = bridge
.run_py_to_json(
r#"
from skyfield.api import load, load_constellation_map
constellation_at = load_constellation_map()
ts = load.timescale()
t = ts.tt_jd(2458849.5) # 2020-01-01
eph = load('de421.bsp')
earth = eph['earth']
results = []
for name in ['mars', 'jupiter barycenter', 'saturn barycenter']:
astrometric = earth.at(t).observe(eph[name])
const_name = constellation_at(astrometric)
ra, dec, _ = astrometric.radec()
results.append(f"{name}:{const_name}:{ra.hours:.6f}:{dec.degrees:.6f}")
rust.collect_string('|'.join(results))
"#,
)
.expect("Failed to run Python code");
let result_str = parse_string(&py_result);
for entry in result_str.split('|') {
let parts: Vec<&str> = entry.split(':').collect();
let planet_name = parts[0];
let py_constellation = parts[1];
let ra_hours: f64 = parts[2].parse().unwrap();
let dec_deg: f64 = parts[3].parse().unwrap();
let pos = crate::positions::Position::barycentric(
direction_from_ra_dec(ra_hours, dec_deg),
nalgebra::Vector3::zeros(),
0,
);
let rust_constellation = map.constellation_of(&pos, &ts);
assert_eq!(
rust_constellation, py_constellation,
"{planet_name}: rust={rust_constellation} python={py_constellation}"
);
}
}
fn direction_from_ra_dec(ra_hours: f64, dec_deg: f64) -> nalgebra::Vector3<f64> {
let ra_rad = ra_hours * std::f64::consts::PI / 12.0;
let dec_rad = dec_deg * std::f64::consts::PI / 180.0;
let cos_dec = dec_rad.cos();
nalgebra::Vector3::new(
cos_dec * ra_rad.cos(),
cos_dec * ra_rad.sin(),
dec_rad.sin(),
)
}
}