Skip to main content

space_units/quantities/
propulsion.rs

1use super::mechanics::G0_MPS2;
2use super::mechanics::KG_PER_POUND;
3use super::DisplayWithUnit;
4use super::Velocity;
5
6// -------------------------
7// Specific impulse
8// -------------------------
9
10/// A specific impulse quantity, stored canonically in seconds (s).
11///
12/// # Construction
13/// ```
14/// use space_units::SpecificImpulse;
15/// let isp = SpecificImpulse::from_s(311.0); // RS-25 sea level
16/// ```
17///
18/// # Special methods
19/// - [`SpecificImpulse::effective_exhaust_velocity`] returns Isp * g0 as a [`Velocity`](crate::Velocity).
20#[must_use]
21#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
22pub struct SpecificImpulse(pub(crate) f64);
23
24/// Display/conversion units for [`SpecificImpulse`].
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum SpecificImpulseUnit {
27    /// Seconds (s) -- conventional Isp unit.
28    Second,
29    /// Newton-seconds per kilogram (N-s/kg) -- equivalent to effective exhaust velocity.
30    NewtonSecondPerKg,
31}
32
33impl SpecificImpulseUnit {
34    const fn symbol(self) -> &'static str {
35        match self {
36            Self::Second => "s",
37            Self::NewtonSecondPerKg => "N*s/kg",
38        }
39    }
40}
41
42impl SpecificImpulse {
43    /// Create from seconds (s).
44    pub const fn from_s(val: f64) -> Self {
45        Self(val)
46    }
47
48    /// Create from N-s/kg (divide by g0 to get seconds).
49    pub const fn from_n_s_per_kg(val: f64) -> Self {
50        Self(val / G0_MPS2)
51    }
52
53    /// Get value in seconds (s).
54    pub const fn in_s(self) -> f64 {
55        self.0
56    }
57
58    /// Get value in N-s/kg (multiply by g0).
59    pub const fn in_n_s_per_kg(self) -> f64 {
60        self.0 * G0_MPS2
61    }
62
63    /// Effective exhaust velocity: Isp * g0, returned as a [`Velocity`](crate::Velocity).
64    pub const fn effective_exhaust_velocity(self) -> Velocity {
65        Velocity::from_mps(self.0 * G0_MPS2)
66    }
67
68    /// Return a display wrapper that formats this specific impulse in the given unit.
69    pub const fn display_as(self, unit: SpecificImpulseUnit) -> DisplayWithUnit {
70        match unit {
71            SpecificImpulseUnit::Second => DisplayWithUnit {
72                value: self.0,
73                symbol: unit.symbol(),
74            },
75            SpecificImpulseUnit::NewtonSecondPerKg => DisplayWithUnit {
76                value: self.in_n_s_per_kg(),
77                symbol: unit.symbol(),
78            },
79        }
80    }
81
82    /// Return the absolute value of this specific impulse.
83    pub fn abs(self) -> Self {
84        Self(self.0.abs())
85    }
86}
87
88impl_quantity_display!(SpecificImpulse, "s");
89
90impl_common_ops!(SpecificImpulse);
91
92// -------------------------
93// Mass flow rate
94// -------------------------
95
96/// A mass flow rate quantity, stored canonically in kilograms per second (kg/s).
97///
98/// # Construction
99/// ```
100/// use space_units::MassFlowRate;
101/// let mdot = MassFlowRate::from_kgps(514.0); // RS-25 propellant flow
102/// ```
103///
104/// # Typed arithmetic
105/// - [`MassFlowRate`] × [`Velocity`](crate::Velocity) → [`Force`](crate::Force)
106#[must_use]
107#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Default)]
108pub struct MassFlowRate(pub(crate) f64);
109
110/// Display/conversion units for [`MassFlowRate`].
111#[derive(Debug, Clone, Copy, PartialEq, Eq)]
112pub enum MassFlowRateUnit {
113    /// Kilograms per second (kg/s) -- SI derived unit.
114    KgPerS,
115    /// Grams per second (g/s).
116    GramPerS,
117    /// Pounds per second (lb/s).
118    PoundPerS,
119    /// Kilograms per hour (kg/h).
120    KgPerH,
121}
122
123impl MassFlowRateUnit {
124    const fn symbol(self) -> &'static str {
125        match self {
126            Self::KgPerS => "kg/s",
127            Self::GramPerS => "g/s",
128            Self::PoundPerS => "lb/s",
129            Self::KgPerH => "kg/h",
130        }
131    }
132
133    const fn kgps_per_unit(self) -> f64 {
134        match self {
135            Self::KgPerS => 1.0,
136            Self::GramPerS => 1e-3,
137            Self::PoundPerS => KG_PER_POUND,
138            Self::KgPerH => 1.0 / 3_600.0,
139        }
140    }
141}
142
143impl MassFlowRate {
144    /// Create from kilograms per second (kg/s).
145    pub const fn from_kgps(val: f64) -> Self {
146        Self(val)
147    }
148
149    /// Create from grams per second (g/s).
150    pub const fn from_gps(val: f64) -> Self {
151        Self(val * 1e-3)
152    }
153
154    /// Create from pounds per second (lb/s).
155    pub const fn from_lbps(val: f64) -> Self {
156        Self(val * KG_PER_POUND)
157    }
158
159    /// Create from kilograms per hour (kg/h).
160    pub const fn from_kgph(val: f64) -> Self {
161        Self(val / 3_600.0)
162    }
163
164    /// Get value in kilograms per second (kg/s).
165    pub const fn in_kgps(self) -> f64 {
166        self.0
167    }
168
169    /// Get value in grams per second (g/s).
170    pub const fn in_gps(self) -> f64 {
171        self.0 / 1e-3
172    }
173
174    /// Get value in pounds per second (lb/s).
175    pub const fn in_lbps(self) -> f64 {
176        self.0 / KG_PER_POUND
177    }
178
179    /// Get value in kilograms per hour (kg/h).
180    pub const fn in_kgph(self) -> f64 {
181        self.0 * 3_600.0
182    }
183
184    /// Get value in the specified [`MassFlowRateUnit`].
185    pub fn in_unit(self, unit: MassFlowRateUnit) -> f64 {
186        self.0 / unit.kgps_per_unit()
187    }
188
189    /// Return a display wrapper that formats this mass flow rate in the given unit.
190    pub fn display_as(self, unit: MassFlowRateUnit) -> DisplayWithUnit {
191        DisplayWithUnit {
192            value: self.in_unit(unit),
193            symbol: unit.symbol(),
194        }
195    }
196
197    /// Return the absolute value of this mass flow rate.
198    pub fn abs(self) -> Self {
199        Self(self.0.abs())
200    }
201}
202
203impl_quantity_display!(MassFlowRate, "kg/s");
204
205impl_common_ops!(MassFlowRate);