1use anise::analysis::prelude::OrbitalElement;
20use arrow::datatypes::{DataType, Field};
21use core::fmt;
22
23use serde::{Deserialize, Serialize};
24use std::collections::HashMap;
25
26#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
28#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
29pub enum StateParameter {
30 Element(OrbitalElement),
31 BdotR,
33 BdotT,
35 BLTOF,
37 Cd,
39 Cr,
41 DryMass,
43 Epoch,
45 GuidanceMode,
47 Isp,
49 PropMass,
51 Thrust,
53 TotalMass,
55}
56
57impl StateParameter {
58 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 Self::DryMass | Self::PropMass => 1e-3,
72 _ => unimplemented!("{self} cannot be used for targeting"),
73 }
74 }
75
76 pub const fn is_b_plane(&self) -> bool {
78 matches!(&self, Self::BdotR | Self::BdotT | Self::BLTOF)
79 }
80
81 pub const fn is_orbital(&self) -> bool {
83 matches!(self, Self::Element(..))
84 }
85
86 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 pub(crate) fn to_field(self, more_meta: Option<Vec<(String, String)>>) -> Field {
116 self.to_field_generic(false, more_meta)
117 }
118
119 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 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}