Skip to main content

nyx_space/od/ground_station/
python.rs

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