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