#[pyfunction]
#[pyo3(text_signature = "(a_or_oe)")]
#[pyo3(name = "orbital_period")]
fn py_orbital_period(a_or_oe: &Bound<'_, PyAny>) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
return Ok(orbits::orbital_period(a));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::orbital_period(oe[0]))
}
#[pyfunction]
#[pyo3(text_signature = "(a_or_oe, gm)")]
#[pyo3(name = "orbital_period_general")]
fn py_orbital_period_general(a_or_oe: &Bound<'_, PyAny>, gm: f64) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
return Ok(orbits::orbital_period_general(a, gm));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::orbital_period_general(oe[0], gm))
}
#[pyfunction]
#[pyo3(text_signature = "(state_eci, gm)")]
#[pyo3(name = "orbital_period_from_state")]
fn py_orbital_period_from_state(
_py: Python,
state_eci: PyReadonlyArray1<f64>,
gm: f64,
) -> PyResult<f64> {
let state = state_eci.as_array();
if state.len() != 6 {
return Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
"state_eci must be a 6-element array [x, y, z, vx, vy, vz]",
));
}
let state_vec = nalgebra::Vector6::from_iterator(state.iter().copied());
Ok(orbits::orbital_period_from_state(&state_vec, gm))
}
#[pyfunction]
#[pyo3(text_signature = "(a_or_oe, angle_format)")]
#[pyo3(name = "mean_motion")]
fn py_mean_motion(a_or_oe: &Bound<'_, PyAny>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
return Ok(orbits::mean_motion(a, angle_format.value));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::mean_motion(oe[0], angle_format.value))
}
#[pyfunction]
#[pyo3(text_signature = "(a_or_oe, gm, angle_format)")]
#[pyo3(name = "mean_motion_general")]
fn py_mean_motion_general(a_or_oe: &Bound<'_, PyAny>, gm: f64, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
return Ok(orbits::mean_motion_general(a, gm, angle_format.value));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::mean_motion_general(oe[0], gm, angle_format.value))
}
#[pyfunction]
#[pyo3(text_signature = "(n, angle_format)")]
#[pyo3(name = "semimajor_axis")]
fn py_semimajor_axis(n: f64, angle_format: &PyAngleFormat) -> PyResult<f64> {
Ok(orbits::semimajor_axis(n, angle_format.value))
}
#[pyfunction]
#[pyo3(text_signature = "(n, gm, angle_format)")]
#[pyo3(name = "semimajor_axis_general")]
fn py_semimajor_axis_general(n: f64, gm: f64, angle_format: &PyAngleFormat) -> PyResult<f64> {
Ok(orbits::semimajor_axis_general(n, gm, angle_format.value))
}
#[pyfunction]
#[pyo3(text_signature = "(period, gm)")]
#[pyo3(name = "semimajor_axis_from_orbital_period_general")]
fn py_semimajor_axis_from_orbital_period_general(period: f64, gm: f64) -> PyResult<f64> {
Ok(orbits::semimajor_axis_from_orbital_period_general(period, gm))
}
#[pyfunction]
#[pyo3(text_signature = "(period)")]
#[pyo3(name = "semimajor_axis_from_orbital_period")]
fn py_semimajor_axis_from_orbital_period(period: f64) -> PyResult<f64> {
Ok(orbits::semimajor_axis_from_orbital_period(period))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None), text_signature = "(a_or_oe, e=None)")]
#[pyo3(name = "perigee_velocity")]
fn py_perigee_velocity(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::perigee_velocity(a, ecc));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::perigee_velocity(oe[0], oe[1]))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None, *, gm), text_signature = "(a_or_oe, e=None, *, gm)")]
#[pyo3(name = "periapsis_velocity")]
fn py_periapsis_velocity(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>, gm: f64) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::periapsis_velocity(a, ecc, gm));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::periapsis_velocity(oe[0], oe[1], gm))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None), text_signature = "(a_or_oe, e=None)")]
#[pyo3(name = "periapsis_distance")]
fn py_periapsis_distance(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::periapsis_distance(a, ecc));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::periapsis_distance(oe[0], oe[1]))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None), text_signature = "(a_or_oe, e=None)")]
#[pyo3(name = "apogee_velocity")]
fn py_apogee_velocity(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::apogee_velocity(a, ecc));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::apogee_velocity(oe[0], oe[1]))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None, *, gm), text_signature = "(a_or_oe, e=None, *, gm)")]
#[pyo3(name = "apoapsis_velocity")]
fn py_apoapsis_velocity(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>, gm: f64) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::apoapsis_velocity(a, ecc, gm));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::apoapsis_velocity(oe[0], oe[1], gm))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None), text_signature = "(a_or_oe, e=None)")]
#[pyo3(name = "apoapsis_distance")]
fn py_apoapsis_distance(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::apoapsis_distance(a, ecc));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::apoapsis_distance(oe[0], oe[1]))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None, *, r_body), text_signature = "(a_or_oe, e=None, *, r_body)")]
#[pyo3(name = "periapsis_altitude")]
fn py_periapsis_altitude(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>, r_body: f64) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::periapsis_altitude(a, ecc, r_body));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::periapsis_altitude(oe[0], oe[1], r_body))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None), text_signature = "(a_or_oe, e=None)")]
#[pyo3(name = "perigee_altitude")]
fn py_perigee_altitude(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::perigee_altitude(a, ecc));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::perigee_altitude(oe[0], oe[1]))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None, *, r_body), text_signature = "(a_or_oe, e=None, *, r_body)")]
#[pyo3(name = "apoapsis_altitude")]
fn py_apoapsis_altitude(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>, r_body: f64) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::apoapsis_altitude(a, ecc, r_body));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::apoapsis_altitude(oe[0], oe[1], r_body))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None), text_signature = "(a_or_oe, e=None)")]
#[pyo3(name = "apogee_altitude")]
fn py_apogee_altitude(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::apogee_altitude(a, ecc));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::apogee_altitude(oe[0], oe[1]))
}
#[pyfunction]
#[pyo3(signature = (a_or_oe, e=None, *, angle_format), text_signature = "(a_or_oe, e=None, *, angle_format)")]
#[pyo3(name = "sun_synchronous_inclination")]
fn py_sun_synchronous_inclination(a_or_oe: &Bound<'_, PyAny>, e: Option<f64>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(a) = a_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'a_or_oe' is a scalar"
))?;
return Ok(orbits::sun_synchronous_inclination(a, ecc, angle_format.value));
}
let oe = pyany_to_f64_array1(a_or_oe, Some(6))?;
Ok(orbits::sun_synchronous_inclination(oe[0], oe[1], angle_format.value))
}
#[pyfunction]
#[pyo3(signature = (), text_signature = "()")]
#[pyo3(name = "geo_sma")]
fn py_geo_sma() -> PyResult<f64> {
Ok(orbits::geo_sma())
}
#[pyfunction]
#[pyo3(signature = (anm_ecc_or_oe, e=None, *, angle_format), text_signature = "(anm_ecc_or_oe, e=None, *, angle_format)")]
#[pyo3(name = "anomaly_eccentric_to_mean")]
fn py_anomaly_eccentric_to_mean(anm_ecc_or_oe: &Bound<'_, PyAny>, e: Option<f64>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(anm_ecc) = anm_ecc_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'anm_ecc_or_oe' is a scalar"
))?;
return Ok(orbits::anomaly_eccentric_to_mean(anm_ecc, ecc, angle_format.value));
}
let oe = pyany_to_f64_array1(anm_ecc_or_oe, Some(6))?;
Ok(orbits::anomaly_eccentric_to_mean(oe[5], oe[1], angle_format.value))
}
#[pyfunction]
#[pyo3(signature = (anm_mean_or_oe, e=None, *, angle_format), text_signature = "(anm_mean_or_oe, e=None, *, angle_format)")]
#[pyo3(name = "anomaly_mean_to_eccentric")]
fn py_anomaly_mean_to_eccentric(anm_mean_or_oe: &Bound<'_, PyAny>, e: Option<f64>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(anm_mean) = anm_mean_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'anm_mean_or_oe' is a scalar"
))?;
return match orbits::anomaly_mean_to_eccentric(anm_mean, ecc, angle_format.value) {
Ok(value) => Ok(value),
Err(err) => Err(exceptions::PyRuntimeError::new_err(err)),
};
}
let oe = pyany_to_f64_array1(anm_mean_or_oe, Some(6))?;
match orbits::anomaly_mean_to_eccentric(oe[5], oe[1], angle_format.value) {
Ok(value) => Ok(value),
Err(err) => Err(exceptions::PyRuntimeError::new_err(err)),
}
}
#[pyfunction]
#[pyo3(signature = (anm_true_or_oe, e=None, *, angle_format), text_signature = "(anm_true_or_oe, e=None, *, angle_format)")]
#[pyo3(name = "anomaly_true_to_eccentric")]
fn py_anomaly_true_to_eccentric(anm_true_or_oe: &Bound<'_, PyAny>, e: Option<f64>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(anm_true) = anm_true_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'anm_true_or_oe' is a scalar"
))?;
return Ok(orbits::anomaly_true_to_eccentric(anm_true, ecc, angle_format.value));
}
let oe = pyany_to_f64_array1(anm_true_or_oe, Some(6))?;
Ok(orbits::anomaly_true_to_eccentric(oe[5], oe[1], angle_format.value))
}
#[pyfunction]
#[pyo3(signature = (anm_ecc_or_oe, e=None, *, angle_format), text_signature = "(anm_ecc_or_oe, e=None, *, angle_format)")]
#[pyo3(name = "anomaly_eccentric_to_true")]
fn py_anomaly_eccentric_to_true(anm_ecc_or_oe: &Bound<'_, PyAny>, e: Option<f64>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(anm_ecc) = anm_ecc_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'anm_ecc_or_oe' is a scalar"
))?;
return Ok(orbits::anomaly_eccentric_to_true(anm_ecc, ecc, angle_format.value));
}
let oe = pyany_to_f64_array1(anm_ecc_or_oe, Some(6))?;
Ok(orbits::anomaly_eccentric_to_true(oe[5], oe[1], angle_format.value))
}
#[pyfunction]
#[pyo3(signature = (anm_true_or_oe, e=None, *, angle_format), text_signature = "(anm_true_or_oe, e=None, *, angle_format)")]
#[pyo3(name = "anomaly_true_to_mean")]
fn py_anomaly_true_to_mean(anm_true_or_oe: &Bound<'_, PyAny>, e: Option<f64>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(anm_true) = anm_true_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'anm_true_or_oe' is a scalar"
))?;
return Ok(orbits::anomaly_true_to_mean(anm_true, ecc, angle_format.value));
}
let oe = pyany_to_f64_array1(anm_true_or_oe, Some(6))?;
Ok(orbits::anomaly_true_to_mean(oe[5], oe[1], angle_format.value))
}
#[pyfunction]
#[pyo3(signature = (anm_mean_or_oe, e=None, *, angle_format), text_signature = "(anm_mean_or_oe, e=None, *, angle_format)")]
#[pyo3(name = "anomaly_mean_to_true")]
fn py_anomaly_mean_to_true(anm_mean_or_oe: &Bound<'_, PyAny>, e: Option<f64>, angle_format: &PyAngleFormat) -> PyResult<f64> {
if let Ok(anm_mean) = anm_mean_or_oe.extract::<f64>() {
let ecc = e.ok_or_else(|| exceptions::PyValueError::new_err(
"Parameter 'e' is required when 'anm_mean_or_oe' is a scalar"
))?;
return match orbits::anomaly_mean_to_true(anm_mean, ecc, angle_format.value) {
Ok(value) => Ok(value),
Err(err) => Err(exceptions::PyRuntimeError::new_err(err)),
};
}
let oe = pyany_to_f64_array1(anm_mean_or_oe, Some(6))?;
match orbits::anomaly_mean_to_true(oe[5], oe[1], angle_format.value) {
Ok(value) => Ok(value),
Err(err) => Err(exceptions::PyRuntimeError::new_err(err)),
}
}
#[pyfunction]
#[pyo3(text_signature = "(line1, line2)")]
#[pyo3(name = "validate_tle_lines")]
fn py_validate_tle_lines(line1: String, line2: String) -> PyResult<bool> {
Ok(orbits::validate_tle_lines(&line1, &line2))
}
#[pyfunction]
#[pyo3(text_signature = "(line)")]
#[pyo3(name = "validate_tle_line")]
fn py_validate_tle_line(line: String) -> PyResult<bool> {
Ok(orbits::validate_tle_line(&line))
}
#[pyfunction]
#[pyo3(text_signature = "(line)")]
#[pyo3(name = "calculate_tle_line_checksum")]
fn py_calculate_tle_line_checksum(line: String) -> PyResult<u32> {
Ok(orbits::calculate_tle_line_checksum(&line))
}
#[pyfunction]
#[pyo3(text_signature = "(line1, line2)")]
#[pyo3(name = "keplerian_elements_from_tle")]
fn py_keplerian_elements_from_tle<'py>(py: Python<'py>, line1: String, line2: String) -> PyResult<(PyEpoch, Bound<'py, PyArray<f64, Ix1>>)> {
match orbits::keplerian_elements_from_tle(&line1, &line2) {
Ok((epoch, elements)) => {
let elements_array = elements.as_slice().to_pyarray(py).to_owned();
let py_epoch = PyEpoch { obj: epoch };
Ok((py_epoch, elements_array))
},
Err(e) => Err(exceptions::PyRuntimeError::new_err(e.to_string())),
}
}
#[pyfunction]
#[pyo3(text_signature = "(epoch, elements, norad_id)")]
#[pyo3(name = "keplerian_elements_to_tle")]
fn py_keplerian_elements_to_tle(
epoch: &PyEpoch,
elements: PyReadonlyArray1<f64>,
norad_id: &str,
) -> PyResult<(String, String)> {
let elements_array = elements.as_array();
let elements_vec = na::Vector6::from_row_slice(elements_array.as_slice().unwrap());
match orbits::keplerian_elements_to_tle(
&epoch.obj,
&elements_vec,
norad_id,
) {
Ok((line1, line2)) => Ok((line1, line2)),
Err(e) => Err(exceptions::PyRuntimeError::new_err(e.to_string())),
}
}
#[pyfunction]
#[pyo3(text_signature = "(epoch, inclination, raan, eccentricity, arg_perigee, mean_anomaly, mean_motion, norad_id, ephemeris_type, element_set_number, revolution_number, classification=None, intl_designator=None, first_derivative=None, second_derivative=None, bstar=None)")]
#[pyo3(name = "create_tle_lines")]
#[allow(clippy::too_many_arguments)]
fn py_create_tle_lines(
epoch: &PyEpoch,
inclination: f64,
raan: f64,
eccentricity: f64,
arg_perigee: f64,
mean_anomaly: f64,
mean_motion: f64,
norad_id: &str,
ephemeris_type: u8,
element_set_number: u16,
revolution_number: u32,
classification: Option<char>,
intl_designator: Option<String>,
first_derivative: Option<f64>,
second_derivative: Option<f64>,
bstar: Option<f64>,
) -> PyResult<(String, String)> {
let intl_designator_ref = intl_designator.as_deref();
match orbits::create_tle_lines(
&epoch.obj,
norad_id,
classification.unwrap_or(' '),
intl_designator_ref.unwrap_or(""),
mean_motion,
eccentricity,
inclination,
raan,
arg_perigee,
mean_anomaly,
first_derivative.unwrap_or(0.0),
second_derivative.unwrap_or(0.0),
bstar.unwrap_or(0.0),
ephemeris_type,
element_set_number,
revolution_number,
) {
Ok((line1, line2)) => Ok((line1, line2)),
Err(e) => Err(exceptions::PyRuntimeError::new_err(e.to_string())),
}
}
#[pyfunction]
#[pyo3(text_signature = "(norad_str)")]
#[pyo3(name = "parse_norad_id")]
fn py_parse_norad_id(norad_str: String) -> PyResult<u32> {
match orbits::parse_norad_id(&norad_str) {
Ok(id) => Ok(id),
Err(e) => Err(exceptions::PyRuntimeError::new_err(e.to_string())),
}
}
#[pyfunction]
#[pyo3(text_signature = "(norad_id)")]
#[pyo3(name = "norad_id_numeric_to_alpha5")]
fn py_norad_id_numeric_to_alpha5(norad_id: u32) -> PyResult<String> {
match orbits::norad_id_numeric_to_alpha5(norad_id) {
Ok(alpha5_id) => Ok(alpha5_id),
Err(e) => Err(exceptions::PyRuntimeError::new_err(e.to_string())),
}
}
#[pyfunction]
#[pyo3(text_signature = "(alpha5_id)")]
#[pyo3(name = "norad_id_alpha5_to_numeric")]
fn py_norad_id_alpha5_to_numeric(alpha5_id: String) -> PyResult<u32> {
match orbits::norad_id_alpha5_to_numeric(&alpha5_id) {
Ok(numeric_id) => Ok(numeric_id),
Err(e) => Err(exceptions::PyRuntimeError::new_err(e.to_string())),
}
}
#[pyfunction]
#[pyo3(text_signature = "(line1)")]
#[pyo3(name = "epoch_from_tle")]
fn py_epoch_from_tle(line1: String) -> PyResult<PyEpoch> {
match orbits::epoch_from_tle(&line1) {
Ok(epoch) => Ok(PyEpoch { obj: epoch }),
Err(e) => Err(exceptions::PyRuntimeError::new_err(e.to_string())),
}
}
#[pyfunction]
#[pyo3(text_signature = "(osc, angle_format)")]
#[pyo3(name = "state_koe_osc_to_mean")]
fn py_state_koe_osc_to_mean<'py>(
py: Python<'py>,
osc: &Bound<'_, PyAny>,
angle_format: &PyAngleFormat,
) -> PyResult<Bound<'py, PyArray<f64, Ix1>>> {
let osc_vec = pyany_to_f64_array1(osc, Some(6))?;
let osc_svec = SVector::<f64, 6>::from_row_slice(&osc_vec);
let mean = orbits::state_koe_osc_to_mean(&osc_svec, angle_format.value);
Ok(mean.as_slice().to_pyarray(py))
}
#[pyfunction]
#[pyo3(text_signature = "(mean, angle_format)")]
#[pyo3(name = "state_koe_mean_to_osc")]
fn py_state_koe_mean_to_osc<'py>(
py: Python<'py>,
mean: &Bound<'_, PyAny>,
angle_format: &PyAngleFormat,
) -> PyResult<Bound<'py, PyArray<f64, Ix1>>> {
let mean_vec = pyany_to_f64_array1(mean, Some(6))?;
let mean_svec = SVector::<f64, 6>::from_row_slice(&mean_vec);
let osc = orbits::state_koe_mean_to_osc(&mean_svec, angle_format.value);
Ok(osc.as_slice().to_pyarray(py))
}
#[pyclass(name = "WalkerPattern", module = "brahe._brahe", eq, eq_int, from_py_object)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)] pub enum PyWalkerPattern {
DELTA = 0,
STAR = 1,
}
impl From<PyWalkerPattern> for orbits::WalkerPattern {
fn from(pattern: PyWalkerPattern) -> Self {
match pattern {
PyWalkerPattern::DELTA => orbits::WalkerPattern::Delta,
PyWalkerPattern::STAR => orbits::WalkerPattern::Star,
}
}
}
impl From<orbits::WalkerPattern> for PyWalkerPattern {
fn from(pattern: orbits::WalkerPattern) -> Self {
match pattern {
orbits::WalkerPattern::Delta => PyWalkerPattern::DELTA,
orbits::WalkerPattern::Star => PyWalkerPattern::STAR,
}
}
}
#[pyclass(module = "brahe._brahe", from_py_object)]
#[pyo3(name = "WalkerConstellationGenerator")]
#[derive(Clone)]
pub struct PyWalkerConstellationGenerator {
pub(crate) generator: orbits::WalkerConstellationGenerator,
}
#[pymethods]
impl PyWalkerConstellationGenerator {
#[new]
#[pyo3(signature = (t, p, f, semi_major_axis, eccentricity, inclination, argument_of_perigee, reference_raan, reference_mean_anomaly, epoch, angle_format, pattern))]
#[allow(clippy::too_many_arguments)]
fn new(
t: usize,
p: usize,
f: usize,
semi_major_axis: f64,
eccentricity: f64,
inclination: f64,
argument_of_perigee: f64,
reference_raan: f64,
reference_mean_anomaly: f64,
epoch: &PyEpoch,
angle_format: &PyAngleFormat,
pattern: &PyWalkerPattern,
) -> Self {
Self {
generator: orbits::WalkerConstellationGenerator::new(
t,
p,
f,
semi_major_axis,
eccentricity,
inclination,
argument_of_perigee,
reference_raan,
reference_mean_anomaly,
epoch.obj,
angle_format.value,
orbits::WalkerPattern::from(*pattern),
),
}
}
fn with_base_name(&self, name: &str) -> Self {
Self {
generator: self.generator.clone().with_base_name(name),
}
}
#[getter]
fn total_satellites(&self) -> usize {
self.generator.total_satellites
}
#[getter]
fn num_planes(&self) -> usize {
self.generator.num_planes
}
#[getter]
fn phasing(&self) -> usize {
self.generator.phasing
}
#[getter]
fn satellites_per_plane(&self) -> usize {
self.generator.satellites_per_plane()
}
#[getter]
fn semi_major_axis(&self) -> f64 {
self.generator.semi_major_axis
}
#[getter]
fn eccentricity(&self) -> f64 {
self.generator.eccentricity
}
#[getter]
fn epoch(&self) -> PyEpoch {
PyEpoch {
obj: self.generator.epoch,
}
}
#[getter]
fn pattern(&self) -> PyWalkerPattern {
PyWalkerPattern::from(self.generator.pattern)
}
#[pyo3(signature = (plane_index, sat_index, angle_format))]
fn satellite_elements<'py>(
&self,
py: Python<'py>,
plane_index: usize,
sat_index: usize,
angle_format: &PyAngleFormat,
) -> PyResult<Bound<'py, PyArray<f64, Ix1>>> {
let elements = self.generator.satellite_elements(plane_index, sat_index);
let output = match angle_format.value {
constants::AngleFormat::Degrees => Vector6::new(
elements[0],
elements[1],
elements[2] * constants::RAD2DEG,
elements[3] * constants::RAD2DEG,
elements[4] * constants::RAD2DEG,
elements[5] * constants::RAD2DEG,
),
constants::AngleFormat::Radians => elements,
};
Ok(output.as_slice().to_pyarray(py))
}
#[pyo3(signature = (step_size))]
fn as_keplerian_propagators(&self, step_size: f64) -> Vec<PyKeplerianPropagator> {
self.generator
.as_keplerian_propagators(step_size)
.into_iter()
.map(|p| PyKeplerianPropagator { propagator: p })
.collect()
}
#[pyo3(signature = (step_size, bstar, ndt2, nddt6))]
fn as_sgp_propagators(
&self,
step_size: f64,
bstar: f64,
ndt2: f64,
nddt6: f64,
) -> PyResult<Vec<PySGPPropagator>> {
self.generator
.as_sgp_propagators(step_size, bstar, ndt2, nddt6)
.map(|props| {
props
.into_iter()
.map(|p| PySGPPropagator { propagator: p })
.collect()
})
.map_err(|e| exceptions::PyRuntimeError::new_err(e.to_string()))
}
#[pyo3(signature = (propagation_config, force_config, params=None))]
fn as_numerical_propagators(
&self,
propagation_config: &PyNumericalPropagationConfig,
force_config: &PyForceModelConfig,
params: Option<&Bound<'_, PyAny>>,
) -> PyResult<Vec<PyNumericalOrbitPropagator>> {
let params_dvec = match params {
Some(p) => {
let vec = pyany_to_f64_array1(p, None)?;
Some(DVector::from_vec(vec))
}
None => None,
};
self.generator
.as_numerical_propagators(
propagation_config.config.clone(),
force_config.config.clone(),
params_dvec,
)
.map(|props| {
props
.into_iter()
.map(|p| PyNumericalOrbitPropagator { propagator: p })
.collect()
})
.map_err(|e| exceptions::PyRuntimeError::new_err(e.to_string()))
}
}