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 crate::dynamics::guidance::LocalFrame;
24use serde::{Deserialize, Serialize};
25use serde_dhall::StaticType;
26use std::collections::HashMap;
27
28#[cfg(feature = "python")]
29use pyo3::prelude::*;
30
31#[cfg_attr(feature = "python", pyclass)]
32#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
33#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, StaticType)]
34pub enum StateParameter {
35    Element(OrbitalElement),
36    /// B-Plane B⋅R
37    BdotR(),
38    /// B-Plane B⋅T
39    BdotT(),
40    /// B-Plane LTOF
41    BLTOF(),
42    /// Coefficient of drag
43    Cd(),
44    /// Coefficient of reflectivity
45    Cr(),
46    /// Dry mass (kg)
47    DryMass(),
48    /// The epoch of the state
49    Epoch(),
50    /// Return the guidance mode of the spacecraft
51    GuidanceMode(),
52    /// Specific impulse (isp) in seconds
53    Isp(),
54    /// prop mass in kilograms
55    PropMass(),
56    /// Inertial thrust-direction x component
57    ThrustX(),
58    /// Inertial thrust-direction y component
59    ThrustY(),
60    /// Inertial thrust-direction z component
61    ThrustZ(),
62    /// In-plane thrust angle in degrees for the requested local frame
63    ThrustInPlane(LocalFrame),
64    /// Out-of-plane thrust angle in degrees for the requested local frame
65    ThrustOutOfPlane(LocalFrame),
66    /// Thrust (Newtons)
67    Thrust(),
68    /// Total mass
69    TotalMass(),
70}
71
72impl StateParameter {
73    /// Returns the default event finding precision in the unit of that parameter
74    pub fn default_event_precision(&self) -> f64 {
75        match self {
76            Self::Element(el) => {
77                if el == &OrbitalElement::Period {
78                    1e-1
79                } else {
80                    1e-3
81                }
82            }
83            Self::BdotR() | Self::BdotT() => 1e-3,
84
85            // Special
86            Self::DryMass() | Self::PropMass() => 1e-3,
87            _ => unimplemented!("{self} cannot be used for targeting"),
88        }
89    }
90
91    /// Returns whether this parameter is of the B-Plane kind
92    pub const fn is_b_plane(&self) -> bool {
93        matches!(&self, Self::BdotR() | Self::BdotT() | Self::BLTOF())
94    }
95
96    /// Returns whether this is an orbital parameter
97    pub const fn is_orbital(&self) -> bool {
98        matches!(self, Self::Element(..))
99    }
100
101    /// Returns whether this parameter is only applicable to a spacecraft state
102    pub const fn is_for_spacecraft(&self) -> bool {
103        matches!(
104            &self,
105            Self::DryMass()
106                | Self::PropMass()
107                | Self::Cr()
108                | Self::Cd()
109                | Self::Isp()
110                | Self::GuidanceMode()
111                | Self::ThrustX()
112                | Self::ThrustY()
113                | Self::ThrustZ()
114                | Self::ThrustInPlane(_)
115                | Self::ThrustOutOfPlane(_)
116                | Self::Thrust()
117        )
118    }
119
120    pub const fn unit(&self) -> &'static str {
121        match self {
122            Self::Element(e) => e.unit(),
123            Self::BdotR() | Self::BdotT() => "km",
124
125            Self::DryMass() | Self::PropMass() => "kg",
126            Self::ThrustX() | Self::ThrustY() | Self::ThrustZ() => "unitless",
127            Self::ThrustInPlane(_) | Self::ThrustOutOfPlane(_) => "deg",
128            Self::Isp() => "isp",
129            Self::Thrust() => "N",
130            _ => "",
131        }
132    }
133}
134
135impl StateParameter {
136    /// Returns the parquet field of this parameter
137    pub(crate) fn to_field(self, more_meta: Option<Vec<(String, String)>>) -> Field {
138        self.to_field_generic(false, more_meta)
139    }
140
141    /// Returns the parquet field of this parameter
142    pub(crate) fn to_cov_field(self, more_meta: Option<Vec<(String, String)>>) -> Field {
143        self.to_field_generic(true, more_meta)
144    }
145
146    /// Returns the parquet field of this parameter
147    fn to_field_generic(self, is_sigma: bool, more_meta: Option<Vec<(String, String)>>) -> Field {
148        let mut meta = HashMap::new();
149        meta.insert("unit".to_string(), self.unit().to_string());
150        if let Some(more_data) = more_meta {
151            for (k, v) in more_data {
152                meta.insert(k, v);
153            }
154        }
155
156        Field::new(
157            if is_sigma {
158                format!("Sigma {self}")
159            } else {
160                format!("{self}")
161            },
162            if self == Self::GuidanceMode() {
163                DataType::Utf8
164            } else {
165                DataType::Float64
166            },
167            false,
168        )
169        .with_metadata(meta)
170    }
171}
172
173impl fmt::Display for StateParameter {
174    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175        let repr = match *self {
176            Self::Element(e) => return write!(f, "{e}"),
177            Self::BLTOF() => "BLToF",
178            Self::BdotR() => "BdotR",
179            Self::BdotT() => "BdotT",
180            Self::Cd() => "cd",
181            Self::Cr() => "cr",
182            Self::DryMass() => "dry_mass",
183            Self::Epoch() => "epoch",
184            Self::GuidanceMode() => "guidance_mode",
185            Self::Isp() => "isp",
186            Self::PropMass() => "prop_mass",
187            Self::ThrustX() => "thrust_x",
188            Self::ThrustY() => "thrust_y",
189            Self::ThrustZ() => "thrust_z",
190            Self::ThrustInPlane(frame) => return write!(f, "thrust_in_plane ({frame:?}) (deg)"),
191            Self::ThrustOutOfPlane(frame) => {
192                return write!(f, "thrust_out_of_plane ({frame:?}) (deg)")
193            }
194            Self::Thrust() => "thrust",
195            Self::TotalMass() => "total_mass",
196        };
197        let unit = if self.unit().is_empty() {
198            String::new()
199        } else {
200            format!(" ({})", self.unit())
201        };
202        write!(f, "{repr}{unit}")
203    }
204}