Skip to main content

nyx_space/md/
param.rs

1/*
2    Nyx, blazing fast astrodynamics
3    Copyright (C) 2018-onwards Christopher Rabotin <christopher.rabotin@gmail.com>
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <https://www.gnu.org/licenses/>.
17*/
18
19use anise::analysis::prelude::OrbitalElement;
20use arrow::datatypes::{DataType, Field};
21use core::fmt;
22
23use serde::{Deserialize, Serialize};
24use serde_dhall::StaticType;
25use std::collections::HashMap;
26
27#[cfg(feature = "python")]
28use pyo3::prelude::*;
29
30#[cfg_attr(feature = "python", pyclass)]
31#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
32#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, StaticType)]
33pub enum StateParameter {
34    Element(OrbitalElement),
35    /// B-Plane B⋅R
36    BdotR(),
37    /// B-Plane B⋅T
38    BdotT(),
39    /// B-Plane LTOF
40    BLTOF(),
41    /// Coefficient of drag
42    Cd(),
43    /// Coefficient of reflectivity
44    Cr(),
45    /// Dry mass (kg)
46    DryMass(),
47    /// The epoch of the state
48    Epoch(),
49    /// Return the guidance mode of the spacecraft
50    GuidanceMode(),
51    /// Specific impulse (isp) in seconds
52    Isp(),
53    /// prop mass in kilograms
54    PropMass(),
55    /// Thrust (Newtons)
56    Thrust(),
57    /// Total mass
58    TotalMass(),
59}
60
61impl StateParameter {
62    /// Returns the default event finding precision in the unit of that parameter
63    pub fn default_event_precision(&self) -> f64 {
64        match self {
65            Self::Element(el) => {
66                if el == &OrbitalElement::Period {
67                    1e-1
68                } else {
69                    1e-3
70                }
71            }
72            Self::BdotR() | Self::BdotT() => 1e-3,
73
74            // Special
75            Self::DryMass() | Self::PropMass() => 1e-3,
76            _ => unimplemented!("{self} cannot be used for targeting"),
77        }
78    }
79
80    /// Returns whether this parameter is of the B-Plane kind
81    pub const fn is_b_plane(&self) -> bool {
82        matches!(&self, Self::BdotR() | Self::BdotT() | Self::BLTOF())
83    }
84
85    /// Returns whether this is an orbital parameter
86    pub const fn is_orbital(&self) -> bool {
87        matches!(self, Self::Element(..))
88    }
89
90    /// Returns whether this parameter is only applicable to a spacecraft state
91    pub const fn is_for_spacecraft(&self) -> bool {
92        matches!(
93            &self,
94            Self::DryMass()
95                | Self::PropMass()
96                | Self::Cr()
97                | Self::Cd()
98                | Self::Isp()
99                | Self::GuidanceMode()
100                | Self::Thrust()
101        )
102    }
103
104    pub const fn unit(&self) -> &'static str {
105        match self {
106            Self::Element(e) => e.unit(),
107            Self::BdotR() | Self::BdotT() => "km",
108
109            Self::DryMass() | Self::PropMass() => "kg",
110            Self::Isp() => "isp",
111            Self::Thrust() => "N",
112            _ => "",
113        }
114    }
115}
116
117impl StateParameter {
118    /// Returns the parquet field of this parameter
119    pub(crate) fn to_field(self, more_meta: Option<Vec<(String, String)>>) -> Field {
120        self.to_field_generic(false, more_meta)
121    }
122
123    /// Returns the parquet field of this parameter
124    pub(crate) fn to_cov_field(self, more_meta: Option<Vec<(String, String)>>) -> Field {
125        self.to_field_generic(true, more_meta)
126    }
127
128    /// Returns the parquet field of this parameter
129    fn to_field_generic(self, is_sigma: bool, more_meta: Option<Vec<(String, String)>>) -> Field {
130        let mut meta = HashMap::new();
131        meta.insert("unit".to_string(), self.unit().to_string());
132        if let Some(more_data) = more_meta {
133            for (k, v) in more_data {
134                meta.insert(k, v);
135            }
136        }
137
138        Field::new(
139            if is_sigma {
140                format!("Sigma {self}")
141            } else {
142                format!("{self}")
143            },
144            if self == Self::GuidanceMode() {
145                DataType::Utf8
146            } else {
147                DataType::Float64
148            },
149            false,
150        )
151        .with_metadata(meta)
152    }
153}
154
155impl fmt::Display for StateParameter {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        let repr = match *self {
158            Self::Element(e) => return write!(f, "{e}"),
159            Self::BLTOF() => "BLToF",
160            Self::BdotR() => "BdotR",
161            Self::BdotT() => "BdotT",
162            Self::Cd() => "cd",
163            Self::Cr() => "cr",
164            Self::DryMass() => "dry_mass",
165            Self::Epoch() => "epoch",
166            Self::GuidanceMode() => "guidance_mode",
167            Self::Isp() => "isp",
168            Self::PropMass() => "prop_mass",
169            Self::Thrust() => "thrust",
170            Self::TotalMass() => "total_mass",
171        };
172        let unit = if self.unit().is_empty() {
173            String::new()
174        } else {
175            format!(" ({})", self.unit())
176        };
177        write!(f, "{repr}{unit}")
178    }
179}