use crate::context::Context;
use crate::coordinate::Origin;
use crate::error::{Error, Result};
use crate::orbit::Orbit;
use crate::propagate::UncertaintyMethod;
use std::ffi::CStr;
#[derive(Debug, Clone, PartialEq)]
pub struct ImpactProbability {
pub method: UncertaintyMethod,
pub orbit_id: String,
pub object_id: String,
pub body: Origin,
pub epoch: crate::Epoch,
pub miss_distance_au: f64,
pub miss_distance_km: f64,
pub effective_radius_au: f64,
pub effective_radius_km: f64,
pub sigma_distance_au: f64,
pub sigma_distance_km: f64,
pub ip_linear: f64,
pub relative_velocity_au_day: f64,
pub ip_second_order: f64,
pub nonlinearity: f64,
pub ip_agm: f64,
pub ip_mc: f64,
pub mc_n_samples: u64,
pub mc_n_impacts: u64,
pub impact_latitude_deg: f64,
pub impact_longitude_deg: f64,
pub impact_altitude_km: f64,
pub mc_confidence_interval: f64,
pub mean_distance_second_order_au: f64,
pub sigma_distance_second_order_au: f64,
pub skewness: f64,
pub gradient: [f64; 6],
pub distance_hessian: [[f64; 6]; 6],
pub agm_components: u64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct BPlane {
pub method: UncertaintyMethod,
pub body: Origin,
pub epoch: crate::Epoch,
pub b_dot_t_km: f64,
pub b_dot_r_km: f64,
pub b_mag_km: f64,
pub v_inf_km_s: f64,
pub effective_radius_km: f64,
pub body_radius_km: f64,
pub cov_b_plane: [f64; 3],
pub semi_major_3sig_km: f64,
pub semi_minor_3sig_km: f64,
pub ellipse_angle_rad: f64,
pub ip_linear: f64,
}
fn method_from_tag(tag: u8) -> UncertaintyMethod {
match tag {
0 => UncertaintyMethod::FirstOrder,
1 => UncertaintyMethod::SecondOrder,
2 => UncertaintyMethod::sigma_point(),
3 => UncertaintyMethod::monte_carlo(1000),
4 => UncertaintyMethod::auto(),
_ => UncertaintyMethod::FirstOrder,
}
}
unsafe fn cstr_to_string(p: *mut std::ffi::c_char) -> String {
if p.is_null() {
String::new()
} else {
unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned()
}
}
impl Context {
pub fn compute_impact_probabilities(
&self,
orbits: &[Orbit],
end_epoch: crate::Epoch,
methods: &[UncertaintyMethod],
body_filter: &[Origin],
) -> Result<Vec<ImpactProbability>> {
let end_mjd_tdb = end_epoch.mjd_tdb()?;
let mut _orbit_keep: Vec<crate::orbit::OrbitFfiKeep> = Vec::with_capacity(orbits.len());
let ffi_orbits: Vec<_> = orbits
.iter()
.map(|o| {
let (ffi, keep) = o.to_ffi_with_keep()?;
_orbit_keep.push(keep);
Ok(ffi)
})
.collect::<Result<Vec<_>>>()?;
let ffi_methods: Vec<_> = methods.iter().map(UncertaintyMethod::to_ffi).collect();
let body_filter_naif: Vec<i32> = body_filter.iter().map(|o| o.naif_id()).collect();
let mut ffi_result = empyrean_sys::EmpyreanImpactProbabilitiesResult {
records: std::ptr::null_mut(),
num_records: 0,
};
let code = unsafe {
empyrean_sys::empyrean_compute_impact_probabilities(
self.as_raw(),
ffi_orbits.as_ptr(),
ffi_orbits.len(),
end_mjd_tdb,
ffi_methods.as_ptr(),
ffi_methods.len(),
if body_filter_naif.is_empty() {
std::ptr::null()
} else {
body_filter_naif.as_ptr()
},
body_filter_naif.len(),
&mut ffi_result,
)
};
if code != 0 {
return Err(Error::capture(code));
}
let n = ffi_result.num_records;
let mut out = Vec::with_capacity(n);
if !ffi_result.records.is_null() && n > 0 {
for i in 0..n {
let rec = unsafe { &*ffi_result.records.add(i) };
let body = Origin::from_naif_id(rec.body_naif_id).ok_or_else(|| {
Error::invalid_input(format!(
"C ABI returned unknown NAIF id for body: {}",
rec.body_naif_id
))
})?;
out.push(ImpactProbability {
method: method_from_tag(rec.method_tag),
orbit_id: unsafe { cstr_to_string(rec.orbit_id) },
object_id: unsafe { cstr_to_string(rec.object_id) },
body,
epoch: crate::Epoch::from_mjd_tdb(rec.epoch_mjd_tdb),
miss_distance_au: rec.miss_distance_au,
miss_distance_km: rec.miss_distance_km,
effective_radius_au: rec.effective_radius_au,
effective_radius_km: rec.effective_radius_km,
sigma_distance_au: rec.sigma_distance_au,
sigma_distance_km: rec.sigma_distance_km,
ip_linear: rec.ip_linear,
relative_velocity_au_day: rec.relative_velocity_au_day,
ip_second_order: rec.ip_second_order,
nonlinearity: rec.nonlinearity,
ip_agm: rec.ip_agm,
ip_mc: rec.ip_mc,
mc_n_samples: rec.mc_n_samples,
mc_n_impacts: rec.mc_n_impacts,
impact_latitude_deg: rec.impact_latitude_deg,
impact_longitude_deg: rec.impact_longitude_deg,
impact_altitude_km: rec.impact_altitude_km,
mc_confidence_interval: rec.mc_confidence_interval,
mean_distance_second_order_au: rec.mean_distance_second_order_au,
sigma_distance_second_order_au: rec.sigma_distance_second_order_au,
skewness: rec.skewness,
gradient: rec.gradient,
distance_hessian: rec.distance_hessian,
agm_components: rec.agm_components,
});
}
}
unsafe { empyrean_sys::empyrean_compute_impact_probabilities_result_free(&mut ffi_result) };
Ok(out)
}
pub fn compute_b_planes(
&self,
orbits: &[Orbit],
end_epoch: crate::Epoch,
methods: &[UncertaintyMethod],
body_filter: &[Origin],
) -> Result<Vec<BPlane>> {
let end_mjd_tdb = end_epoch.mjd_tdb()?;
let mut _orbit_keep: Vec<crate::orbit::OrbitFfiKeep> = Vec::with_capacity(orbits.len());
let ffi_orbits: Vec<_> = orbits
.iter()
.map(|o| {
let (ffi, keep) = o.to_ffi_with_keep()?;
_orbit_keep.push(keep);
Ok(ffi)
})
.collect::<Result<Vec<_>>>()?;
let ffi_methods: Vec<_> = methods.iter().map(UncertaintyMethod::to_ffi).collect();
let body_filter_naif: Vec<i32> = body_filter.iter().map(|o| o.naif_id()).collect();
let mut ffi_result = empyrean_sys::EmpyreanBPlanesResult {
records: std::ptr::null_mut(),
num_records: 0,
};
let code = unsafe {
empyrean_sys::empyrean_compute_b_planes(
self.as_raw(),
ffi_orbits.as_ptr(),
ffi_orbits.len(),
end_mjd_tdb,
ffi_methods.as_ptr(),
ffi_methods.len(),
if body_filter_naif.is_empty() {
std::ptr::null()
} else {
body_filter_naif.as_ptr()
},
body_filter_naif.len(),
&mut ffi_result,
)
};
if code != 0 {
return Err(Error::capture(code));
}
let n = ffi_result.num_records;
let mut out = Vec::with_capacity(n);
if !ffi_result.records.is_null() && n > 0 {
for i in 0..n {
let rec = unsafe { &*ffi_result.records.add(i) };
let body_str = unsafe { cstr_to_string(rec.body) };
let body: Origin = body_str.parse().map_err(|_| {
Error::invalid_input(format!(
"C ABI returned unrecognized body name: {body_str:?}"
))
})?;
out.push(BPlane {
method: method_from_tag(rec.method_tag),
body,
epoch: crate::Epoch::from_mjd_tdb(rec.epoch_mjd_tdb),
b_dot_t_km: rec.b_dot_t_km,
b_dot_r_km: rec.b_dot_r_km,
b_mag_km: rec.b_mag_km,
v_inf_km_s: rec.v_inf_km_s,
effective_radius_km: rec.effective_radius_km,
body_radius_km: rec.body_radius_km,
cov_b_plane: rec.cov_b_plane,
semi_major_3sig_km: rec.semi_major_3sig_km,
semi_minor_3sig_km: rec.semi_minor_3sig_km,
ellipse_angle_rad: rec.ellipse_angle_rad,
ip_linear: rec.ip_linear,
});
}
}
unsafe { empyrean_sys::empyrean_compute_b_planes_result_free(&mut ffi_result) };
Ok(out)
}
}