Skip to main content

nyx_space/od/ground_station/
python.rs

1use super::super::msr::MeasurementType;
2use super::super::noise::StochasticNoise;
3use super::GroundStation;
4use anise::astro::Location;
5use hifitime::Duration;
6use indexmap::{IndexMap, IndexSet};
7use pyo3::prelude::*;
8use std::collections::HashMap;
9
10#[cfg(feature = "python")]
11#[pymethods]
12impl GroundStation {
13    /// Create a new Ground Station.
14    ///
15    /// :type name: str
16    /// :type location: Location
17    /// :type stochastic_noises: dict[MeasurementType, StochasticNoise]
18    /// :type integration_time: Duration | None
19    /// :type light_time_correction: bool | None
20    /// :type timestamp_noise_s: StochasticNoise | None
21    #[new]
22    #[pyo3(signature = (name, location, stochastic_noises, integration_time=None, light_time_correction=false, timestamp_noise_s=None))]
23    fn py_new(
24        name: String,
25        location: Location,
26        stochastic_noises: HashMap<MeasurementType, StochasticNoise>,
27        integration_time: Option<Duration>,
28        light_time_correction: Option<bool>,
29        timestamp_noise_s: Option<StochasticNoise>,
30    ) -> Self {
31        Self {
32            name,
33            location,
34            measurement_types: IndexSet::from_iter(
35                stochastic_noises
36                    .keys()
37                    .copied()
38                    .collect::<Vec<MeasurementType>>(),
39            ),
40            integration_time,
41            light_time_correction: light_time_correction.unwrap_or(false),
42            timestamp_noise_s,
43            stochastic_noises: Some(stochastic_noises.into_iter().collect()),
44        }
45    }
46
47    /// Load GroundStation from a YAML string.
48    ///
49    /// :type yaml_str: str
50    /// :rtype: GroundStation
51    #[classmethod]
52    #[pyo3(name = "from_yaml")]
53    fn py_from_yaml(_cls: &Bound<'_, pyo3::types::PyType>, yaml_str: &str) -> PyResult<Self> {
54        serde_yml::from_str(yaml_str)
55            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))
56    }
57
58    /// :rtype: str
59    #[pyo3(name = "to_yaml")]
60    fn py_to_yaml(&self) -> PyResult<String> {
61        serde_yml::to_string(self)
62            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))
63    }
64
65    /// Load multiple GroundStations from a YAML file.
66    ///
67    /// :type path: str
68    /// :rtype: list[GroundStation]
69    #[classmethod]
70    #[pyo3(name = "load_many_yaml")]
71    fn py_load_many_yaml(_cls: &Bound<'_, pyo3::types::PyType>, path: &str) -> PyResult<Vec<Self>> {
72        use crate::io::ConfigRepr;
73        Self::load_many(path).map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))
74    }
75
76    /// Load multiple GroundStations from a YAML string.
77    ///
78    /// :type yaml_str: str
79    /// :rtype: list[GroundStation]
80    #[classmethod]
81    #[pyo3(name = "loads_many_yaml")]
82    fn py_loads_many_yaml(
83        _cls: &Bound<'_, pyo3::types::PyType>,
84        yaml_str: &str,
85    ) -> PyResult<Vec<Self>> {
86        use crate::io::ConfigRepr;
87        Self::loads_many(yaml_str)
88            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))
89    }
90
91    /// Dump multiple GroundStations to a YAML file.
92    ///
93    /// :type stations: list[GroundStation]
94    /// :type path: str
95    /// :rtype: None
96    #[classmethod]
97    #[pyo3(name = "dump_many_yaml")]
98    fn py_dump_many_yaml(
99        _cls: &Bound<'_, pyo3::types::PyType>,
100        stations: Vec<Self>,
101        path: &str,
102    ) -> PyResult<()> {
103        let s = serde_yml::to_string(&stations)
104            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))?;
105        std::fs::write(path, s).map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))
106    }
107
108    /// Dump multiple GroundStations to a YAML string.
109    ///
110    /// :type stations: list[GroundStation]
111    /// :rtype: str
112    #[classmethod]
113    #[pyo3(name = "dumps_many_yaml")]
114    fn py_dumps_many_yaml(
115        _cls: &Bound<'_, pyo3::types::PyType>,
116        stations: Vec<Self>,
117    ) -> PyResult<String> {
118        serde_yml::to_string(&stations)
119            .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))
120    }
121
122    #[getter]
123    pub fn get_name(&self) -> String {
124        self.name.clone()
125    }
126
127    #[setter]
128    pub fn set_name(&mut self, name: String) {
129        self.name = name;
130    }
131
132    #[getter]
133    pub fn get_location(&self) -> Location {
134        self.location.clone()
135    }
136
137    #[setter]
138    pub fn set_location(&mut self, location: Location) {
139        self.location = location;
140    }
141
142    #[getter]
143    pub fn get_integration_time(&self) -> Option<Duration> {
144        self.integration_time
145    }
146
147    #[setter]
148    pub fn set_integration_time(&mut self, integration_time: Option<Duration>) {
149        self.integration_time = integration_time;
150    }
151
152    #[getter]
153    pub fn get_light_time_correction(&self) -> bool {
154        self.light_time_correction
155    }
156
157    #[setter]
158    pub fn set_light_time_correction(&mut self, light_time_correction: bool) {
159        self.light_time_correction = light_time_correction;
160    }
161
162    #[getter]
163    pub fn get_timestamp_noise_s(&self) -> Option<StochasticNoise> {
164        self.timestamp_noise_s
165    }
166
167    #[setter]
168    pub fn set_timestamp_noise_s(&mut self, noise: Option<StochasticNoise>) {
169        self.timestamp_noise_s = noise;
170    }
171
172    #[getter]
173    pub fn get_measurement_types(&self) -> Vec<MeasurementType> {
174        self.measurement_types.iter().cloned().collect()
175    }
176
177    #[setter]
178    pub fn set_measurement_types(&mut self, types: Vec<MeasurementType>) {
179        self.measurement_types = types.into_iter().collect();
180    }
181
182    /// Add a measurement type with stochastic noise.
183    ///
184    /// :type msr_type: MeasurementType
185    /// :type noise: StochasticNoise
186    /// :rtype: None
187    pub fn add_measurement_type(&mut self, msr_type: MeasurementType, noise: StochasticNoise) {
188        self.measurement_types.insert(msr_type);
189        self.stochastic_noises
190            .get_or_insert_with(IndexMap::new)
191            .insert(msr_type, noise);
192    }
193
194    /// Remove a measurement type.
195    ///
196    /// :type msr_type: MeasurementType
197    /// :rtype: bool
198    pub fn remove_measurement_type(&mut self, msr_type: &MeasurementType) -> bool {
199        // (Note: Requires IndexSet to be used with the `shift_remove` method to maintain order,
200        // fallback to `.remove()` if order preservation upon deletion is not strictly required)
201        self.measurement_types.shift_remove(msr_type)
202    }
203
204    /// Clear all measurement types
205    ///
206    /// :rtype: None
207    pub fn clear_measurement_types(&mut self) {
208        self.measurement_types.clear();
209    }
210
211    #[getter]
212    pub fn get_stochastic_noises(&self) -> Option<Vec<(MeasurementType, StochasticNoise)>> {
213        self.stochastic_noises
214            .as_ref()
215            .map(|map| map.iter().map(|(k, v)| (*k, *v)).collect())
216    }
217
218    /// Get stochastic noise for a measurement type.
219    ///
220    /// :type m_type: MeasurementType
221    /// :rtype: StochasticNoise | None
222    pub fn get_stochastic_noise(&self, m_type: &MeasurementType) -> Option<StochasticNoise> {
223        self.stochastic_noises
224            .as_ref()
225            .and_then(|map| map.get(m_type).cloned())
226    }
227
228    /// Set stochastic noise for a measurement type.
229    ///
230    /// :type m_type: MeasurementType
231    /// :type noise: StochasticNoise
232    /// :rtype: None
233    pub fn set_stochastic_noise(&mut self, m_type: MeasurementType, noise: StochasticNoise) {
234        self.stochastic_noises
235            .get_or_insert_with(indexmap::IndexMap::new)
236            .insert(m_type, noise);
237    }
238
239    /// Remove stochastic noise for a measurement type.
240    ///
241    /// :type m_type: MeasurementType
242    /// :rtype: StochasticNoise | None
243    pub fn remove_stochastic_noise(&mut self, m_type: &MeasurementType) -> Option<StochasticNoise> {
244        self.stochastic_noises
245            .as_mut()
246            .and_then(|map| map.shift_remove(m_type))
247    }
248
249    /// Clear stochastic noises
250    ///
251    /// :rtype: None
252    pub fn clear_stochastic_noises(&mut self) {
253        self.stochastic_noises = None;
254    }
255
256    fn __str__(&self) -> String {
257        format!("{self}")
258    }
259
260    fn __repr__(&self) -> String {
261        format!("{self:?} @ {self:p}")
262    }
263
264    fn __eq__(&self, other: &Self) -> bool {
265        self == other
266    }
267}