#[pyclass(module = "brahe._brahe", from_py_object)]
#[pyo3(name = "TimeSystem")]
#[derive(Clone)]
pub struct PyTimeSystem {
pub(crate) ts: time::TimeSystem,
}
#[pymethods]
impl PyTimeSystem {
#[classattr]
#[allow(non_snake_case)]
fn GPS() -> Self {
PyTimeSystem { ts: time::TimeSystem::GPS }
}
#[classattr]
#[allow(non_snake_case)]
fn TAI() -> Self {
PyTimeSystem { ts: time::TimeSystem::TAI }
}
#[classattr]
#[allow(non_snake_case)]
fn TT() -> Self {
PyTimeSystem { ts: time::TimeSystem::TT }
}
#[classattr]
#[allow(non_snake_case)]
fn UTC() -> Self {
PyTimeSystem { ts: time::TimeSystem::UTC }
}
#[classattr]
#[allow(non_snake_case)]
fn UT1() -> Self {
PyTimeSystem { ts: time::TimeSystem::UT1 }
}
#[classattr]
#[allow(non_snake_case)]
fn TDB() -> Self {
PyTimeSystem { ts: time::TimeSystem::TDB }
}
#[classattr]
#[allow(non_snake_case)]
fn TCG() -> Self {
PyTimeSystem { ts: time::TimeSystem::TCG }
}
#[classattr]
#[allow(non_snake_case)]
fn TCB() -> Self {
PyTimeSystem { ts: time::TimeSystem::TCB }
}
#[classattr]
#[allow(non_snake_case)]
fn BDT() -> Self {
PyTimeSystem { ts: time::TimeSystem::BDT }
}
#[classattr]
#[allow(non_snake_case)]
fn GST() -> Self {
PyTimeSystem { ts: time::TimeSystem::GST }
}
fn __str__(&self) -> String {
format!("{}", self.ts)
}
fn __repr__(&self) -> String {
format!("TimeSystem.{}", self.ts)
}
fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
match op {
CompareOp::Eq => Ok(self.ts == other.ts),
CompareOp::Ne => Ok(self.ts != other.ts),
_ => Err(exceptions::PyNotImplementedError::new_err("Comparison not supported")),
}
}
}
#[pyfunction]
#[pyo3(text_signature = "(year, month, day, hour, minute, second, nanosecond)")]
#[pyo3(name = "datetime_to_jd")]
fn py_datetime_to_jd(
year: u32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: f64,
nanosecond: f64,
) -> PyResult<f64> {
Ok(time::datetime_to_jd(
year, month, day, hour, minute, second, nanosecond,
))
}
#[pyfunction]
#[pyo3(text_signature = "(year, month, day, hour, minute, second, nanosecond)")]
#[pyo3(name = "datetime_to_mjd")]
fn py_datetime_to_mjd(
year: u32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: f64,
nanosecond: f64,
) -> PyResult<f64> {
Ok(time::datetime_to_mjd(
year, month, day, hour, minute, second, nanosecond,
))
}
#[pyfunction]
#[pyo3(text_signature = "(jd)")]
#[pyo3(name = "jd_to_datetime")]
fn py_jd_to_datetime(jd: f64) -> PyResult<(u32, u8, u8, u8, u8, f64, f64)> {
Ok(time::jd_to_datetime(jd))
}
#[pyfunction]
#[pyo3(text_signature = "(mjd)")]
#[pyo3(name = "mjd_to_datetime")]
fn py_mjd_to_datetime(mjd: f64) -> PyResult<(u32, u8, u8, u8, u8, f64, f64)> {
Ok(time::mjd_to_datetime(mjd))
}
#[pyfunction]
#[pyo3(text_signature = "(mjd, time_system_src, time_system_dst)")]
#[pyo3(name = "time_system_offset_for_mjd")]
fn py_time_system_offset_for_mjd(
mjd: f64,
time_system_src: PyRef<PyTimeSystem>,
time_system_dst: PyRef<PyTimeSystem>,
) -> PyResult<f64> {
Ok(time::time_system_offset_for_mjd(mjd, time_system_src.ts, time_system_dst.ts))
}
#[pyfunction]
#[pyo3(text_signature = "(jd, time_system_src, time_system_dst)")]
#[pyo3(name = "time_system_offset_for_jd")]
fn py_time_system_offset_for_jd(
jd: f64,
time_system_src: PyRef<PyTimeSystem>,
time_system_dst: PyRef<PyTimeSystem>,
) -> PyResult<f64> {
Ok(time::time_system_offset_for_jd(jd, time_system_src.ts, time_system_dst.ts))
}
#[pyfunction]
#[pyo3(text_signature = "(year, month, day, hour, minute, second, nanosecond, time_system_src, time_system_dst)")]
#[pyo3(name = "time_system_offset_for_datetime")]
#[allow(clippy::too_many_arguments)]
fn py_time_system_offset_for_datetime(
year: u32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: f64,
nanosecond: f64,
time_system_src: PyRef<PyTimeSystem>,
time_system_dst: PyRef<PyTimeSystem>,
) -> PyResult<f64> {
Ok(time::time_system_offset_for_datetime(
year,
month,
day,
hour,
minute,
second,
nanosecond,
time_system_src.ts,
time_system_dst.ts,
))
}
#[pyclass(module = "brahe._brahe", from_py_object)]
#[pyo3(name = "Epoch")]
#[derive(Clone)]
pub struct PyEpoch {
obj: time::Epoch,
}
#[pymethods]
impl PyEpoch {
#[new]
#[pyo3(signature = (*args, time_system=None))]
fn __new__(args: &Bound<'_, PyTuple>, time_system: Option<PyRef<PyTimeSystem>>) -> PyResult<Self> {
let default_ts = time_system.map(|ts| ts.ts).unwrap_or(TimeSystem::UTC);
let len = args.len();
if len == 0 {
return Err(exceptions::PyValueError::new_err(
"No arguments provided for Epoch initialization"
));
}
if len == 1 {
let arg = args.get_item(0)?;
if let Ok(s) = arg.cast::<PyString>() {
let datestr = s.to_str()?;
return match time::Epoch::from_string(datestr) {
Some(epoch) => Ok(PyEpoch { obj: epoch }),
None => Err(exceptions::PyValueError::new_err(
format!("Failed to parse epoch string: {}", datestr)
)),
};
}
if let Ok(dt) = arg.cast::<PyDateTime>() {
let year = dt.get_year() as u32;
let month = dt.get_month();
let day = dt.get_day();
let hour = dt.get_hour();
let minute = dt.get_minute();
let second = dt.get_second() as f64;
let microsecond = dt.get_microsecond() as f64;
let nanosecond = microsecond * 1000.0;
return Ok(PyEpoch {
obj: time::Epoch::from_datetime(
year, month, day, hour, minute, second, nanosecond, default_ts
),
});
}
if let Ok(epoch) = arg.extract::<PyRef<PyEpoch>>() {
return Ok(PyEpoch {
obj: epoch.obj,
});
}
if let Ok(value) = arg.extract::<f64>() {
if value < 1000000.0 {
return Ok(PyEpoch {
obj: time::Epoch::from_mjd(value, default_ts),
});
} else {
return Ok(PyEpoch {
obj: time::Epoch::from_jd(value, default_ts),
});
}
}
return Err(exceptions::PyTypeError::new_err(
"Single argument must be str, float/int (MJD/JD), datetime, or Epoch"
));
}
if len == 3 {
let year = args.get_item(0)?.extract::<u32>()?;
let month = args.get_item(1)?.extract::<u8>()?;
let day = args.get_item(2)?.extract::<u8>()?;
return Ok(PyEpoch {
obj: time::Epoch::from_date(year, month, day, default_ts),
});
}
if len == 7 {
let year = args.get_item(0)?.extract::<u32>()?;
let month = args.get_item(1)?.extract::<u8>()?;
let day = args.get_item(2)?.extract::<u8>()?;
let hour = args.get_item(3)?.extract::<u8>()?;
let minute = args.get_item(4)?.extract::<u8>()?;
let second = args.get_item(5)?.extract::<f64>()?;
let nanosecond = args.get_item(6)?.extract::<f64>()?;
return Ok(PyEpoch {
obj: time::Epoch::from_datetime(
year, month, day, hour, minute, second, nanosecond, default_ts
),
});
}
if (4..=6).contains(&len) {
let year = args.get_item(0)?.extract::<u32>()?;
let month = args.get_item(1)?.extract::<u8>()?;
let day = args.get_item(2)?.extract::<u8>()?;
let hour = if len >= 4 { args.get_item(3)?.extract::<u8>()? } else { 0 };
let minute = if len >= 5 { args.get_item(4)?.extract::<u8>()? } else { 0 };
let second = if len >= 6 { args.get_item(5)?.extract::<f64>()? } else { 0.0 };
let nanosecond = 0.0;
return Ok(PyEpoch {
obj: time::Epoch::from_datetime(
year, month, day, hour, minute, second, nanosecond, default_ts
),
});
}
Err(exceptions::PyTypeError::new_err(
format!("Invalid number of arguments ({}). Expected 1, 3-7 arguments", len)
))
}
fn __repr__(&self) -> String {
format!("{:?}", self.obj)
}
fn __str__(&self) -> String {
self.obj.to_string()
}
#[getter]
fn time_system(&self) -> PyTimeSystem {
PyTimeSystem { ts: self.obj.time_system }
}
#[classmethod]
fn from_date(
_cls: &Bound<'_, PyType>,
year: u32,
month: u8,
day: u8,
time_system: PyRef<PyTimeSystem>,
) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_date(
year,
month,
day,
time_system.ts,
),
})
}
#[classmethod]
fn from_day_of_year(
_cls: &Bound<'_, PyType>,
year: u32,
day_of_year: f64,
time_system: PyRef<PyTimeSystem>,
) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_day_of_year(
year,
day_of_year,
time_system.ts,
),
})
}
#[classmethod]
#[allow(clippy::too_many_arguments)]
pub fn from_datetime(
_cls: &Bound<'_, PyType>,
year: u32,
month: u8,
day: u8,
hour: u8,
minute: u8,
second: f64,
nanosecond: f64,
time_system: PyRef<PyTimeSystem>,
) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_datetime(
year,
month,
day,
hour,
minute,
second,
nanosecond,
time_system.ts,
),
})
}
#[classmethod]
pub fn from_string(_cls: &Bound<'_, PyType>, datestr: &str) -> PyResult<PyEpoch> {
match time::Epoch::from_string(datestr) {
Some(epoch) => Ok(PyEpoch { obj: epoch }),
None => Err(exceptions::PyValueError::new_err(
format!("Failed to parse epoch string: {}", datestr)
)),
}
}
#[classmethod]
pub fn from_jd(_cls: &Bound<'_, PyType>, jd: f64, time_system: PyRef<PyTimeSystem>) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_jd(jd, time_system.ts),
})
}
#[classmethod]
pub fn from_mjd(_cls: &Bound<'_, PyType>, mjd: f64, time_system: PyRef<PyTimeSystem>) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_mjd(mjd, time_system.ts),
})
}
#[classmethod]
pub fn from_gps_date(_cls: &Bound<'_, PyType>, week: u32, seconds: f64) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_gps_date(week, seconds),
})
}
#[classmethod]
pub fn from_gps_seconds(_cls: &Bound<'_, PyType>, gps_seconds: f64) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_gps_seconds(gps_seconds),
})
}
#[classmethod]
pub fn from_gps_nanoseconds(_cls: &Bound<'_, PyType>, gps_nanoseconds: u64) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_gps_nanoseconds(gps_nanoseconds),
})
}
#[classmethod]
pub fn from_unix_timestamp(_cls: &Bound<'_, PyType>, timestamp: f64) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::from_unix_timestamp(timestamp),
})
}
#[classmethod]
pub fn now(_cls: &Bound<'_, PyType>) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: time::Epoch::now(),
})
}
pub fn to_datetime_as_time_system(&self, time_system: PyRef<PyTimeSystem>) -> (u32, u8, u8, u8, u8, f64, f64) {
self.obj
.to_datetime_as_time_system(time_system.ts)
}
pub fn to_datetime(&self) -> (u32, u8, u8, u8, u8, f64, f64) {
self.obj.to_datetime()
}
pub fn to_pydatetime<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDateTime>> {
let (year, month, day, hour, minute, second, nanosecond) =
self.obj.to_datetime_as_time_system(time::TimeSystem::UTC);
let whole_seconds = second.floor() as u8;
let fractional_seconds = second - second.floor();
let microsecond = ((fractional_seconds * 1e6) + (nanosecond / 1e3)).round() as u32;
let datetime_mod = py.import("datetime")?;
let timezone_utc = datetime_mod.getattr("timezone")?.getattr("utc")?;
let tzinfo = timezone_utc.cast::<pyo3::types::PyTzInfo>()?;
PyDateTime::new(
py,
year as i32,
month,
day,
hour,
minute,
whole_seconds,
microsecond,
Some(tzinfo)
)
}
pub fn jd_as_time_system(&self, time_system: PyRef<PyTimeSystem>) -> f64 {
self.obj
.jd_as_time_system(time_system.ts)
}
pub fn jd(&self) -> f64 {
self.obj.jd()
}
pub fn mjd_as_time_system(&self, time_system: PyRef<PyTimeSystem>) -> f64 {
self.obj
.mjd_as_time_system(time_system.ts)
}
pub fn mjd(&self) -> f64 {
self.obj.mjd()
}
pub fn gps_date(&self) -> (u32, f64) {
self.obj.gps_date()
}
pub fn gps_seconds(&self) -> f64 {
self.obj.gps_seconds()
}
pub fn gps_nanoseconds(&self) -> f64 {
self.obj.gps_nanoseconds()
}
pub fn unix_timestamp(&self) -> f64 {
self.obj.unix_timestamp()
}
pub fn isostring(&self) -> String {
self.obj.isostring()
}
pub fn isostring_with_decimals(&self, decimals: usize) -> String {
self.obj.isostring_with_decimals(decimals)
}
pub fn to_string_as_time_system(&self, time_system: PyRef<PyTimeSystem>) -> String {
self.obj
.to_string_as_time_system(time_system.ts)
}
pub fn gast(&self, angle_format: PyRef<PyAngleFormat>) -> f64 {
self.obj.gast(angle_format.value)
}
pub fn gmst(&self, angle_format: PyRef<PyAngleFormat>) -> f64 {
self.obj.gmst(angle_format.value)
}
pub fn year(&self) -> u32 {
self.obj.year()
}
pub fn month(&self) -> u8 {
self.obj.month()
}
pub fn day(&self) -> u8 {
self.obj.day()
}
pub fn hour(&self) -> u8 {
self.obj.hour()
}
pub fn minute(&self) -> u8 {
self.obj.minute()
}
pub fn second(&self) -> f64 {
self.obj.second()
}
pub fn nanosecond(&self) -> f64 {
self.obj.nanosecond()
}
pub fn day_of_year(&self) -> f64 {
self.obj.day_of_year()
}
pub fn day_of_year_as_time_system(&self, time_system: PyRef<PyTimeSystem>) -> f64 {
self.obj.day_of_year_as_time_system(time_system.ts)
}
pub fn __add__(&self, other: f64) -> PyResult<PyEpoch> {
Ok(PyEpoch {
obj: self.obj + other,
})
}
pub fn __iadd__(&mut self, other: f64) {
self.obj += other;
}
pub fn __sub__<'py>(&self, other: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
if let Ok(epoch) = other.extract::<PyEpoch>() {
let diff = self.obj - epoch.obj;
return diff.into_bound_py_any(other.py());
}
if let Ok(seconds) = other.extract::<f64>() {
let result = PyEpoch {
obj: self.obj - seconds,
};
return Bound::new(other.py(), result).map(|b| b.into_any());
}
Err(PyErr::new::<pyo3::exceptions::PyTypeError, _>(
format!(
"unsupported operand type(s) for -: 'Epoch' and '{}'",
other.get_type().name()?
),
))
}
pub fn __isub__(&mut self, other: f64) {
self.obj -= other;
}
fn __richcmp__(&self, other: &PyEpoch, op: CompareOp) -> bool {
match op {
CompareOp::Eq => self.obj == other.obj,
CompareOp::Ne => self.obj != other.obj,
CompareOp::Ge => self.obj >= other.obj,
CompareOp::Gt => self.obj > other.obj,
CompareOp::Le => self.obj <= other.obj,
CompareOp::Lt => self.obj < other.obj,
}
}
}
#[pyclass(module = "brahe._brahe")]
#[pyo3(name = "TimeRange")]
struct PyTimeRange {
obj: time::TimeRange,
}
#[pymethods]
impl PyTimeRange {
#[new]
fn new(epoch_start: &PyEpoch, epoch_end: &PyEpoch, step: f64) -> Self {
Self {
obj: time::TimeRange::new(epoch_start.obj, epoch_end.obj, step),
}
}
fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
slf
}
fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<PyEpoch> {
slf.obj.next().map(|e| PyEpoch { obj: e })
}
}