capra_core/common/gas.rs
1use crate::common::mtr_bar;
2
3/// Represents errors that occur while working with Gases.
4#[derive(thiserror::Error, Debug)]
5pub enum GasError {
6 #[error("gas does not have total fraction of 1.0")]
7 /// The sum of all percentage gas fractions does not add up to 100.
8 FractionError,
9}
10
11/// A gas mix used in a dive.
12#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
13#[cfg_attr(feature = "use-serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Gas {
15 /// Percentage fraction of oxygen in the mix.
16 o2: usize,
17 /// Percentage fraction of helium in the mix.
18 he: usize,
19 /// Percentage fraction of nitrogen in the mix.
20 n2: usize,
21}
22
23/// Shorthand for creating a Gas, in a style similar to mix notation (O2/He)
24/// # Panics
25/// This macro will panic if the two supplied values exceed 100.
26#[macro_export]
27macro_rules! gas {
28 ($o2:expr, $he:expr) => {{
29 Gas::new($o2, $he, 100 - $o2 - $he).unwrap()
30 }};
31}
32
33impl Gas {
34 /// Returns a new Gas with the given parameters.
35 /// # Arguments
36 /// * `o2` - Percentage fraction of oxygen in the mix.
37 /// * `he` - Percentage fraction of helium in the mix.
38 /// * `n2` - Percentage fraction of nitrogen in the mix.
39 /// # Errors
40 /// This function will return a [`GasError`] if the percentage fractions do not add up to 100.
41 pub fn new(o2: usize, he: usize, n2: usize) -> Result<Self, GasError> {
42 if o2 + he + n2 != 100 {
43 return Err(GasError::FractionError);
44 }
45
46 Ok(Self { o2, he, n2 })
47 }
48
49 /// Returns the **fraction** of nitrogen in the mix.
50 pub fn fr_n2(&self) -> f64 {
51 self.n2 as f64 / 100.0
52 }
53
54 /// Returns the **fraction** of oxygen in the mix.
55 pub fn fr_o2(&self) -> f64 {
56 self.o2 as f64 / 100.0
57 }
58
59 /// Returns the **fraction** of helium in the mix.
60 pub fn fr_he(&self) -> f64 {
61 self.he as f64 / 100.0
62 }
63 /// Returns the percentage fraction of oxygen in the mix.
64 pub fn o2(&self) -> usize {
65 self.o2
66 }
67
68 /// Returns the percentage fraction of helium in the mix.
69 pub fn he(&self) -> usize {
70 self.he
71 }
72
73 /// Returns the percentage fraction of nitrogen in the mix.
74 pub fn n2(&self) -> usize {
75 self.n2
76 }
77
78 /// Returns the Equivalent Narcotic Depth (END) of the mix at a given depth.
79 /// # Arguments
80 /// * `depth` - The depth the mix is being breathed at.
81 pub fn equivalent_narcotic_depth(&self, depth: usize) -> usize {
82 (((depth + 10) as f64 * (1.0 - self.fr_he())) - 10.0) as usize
83 }
84
85 /// Helper function to check whether the mix is in an acceptable ppO2 range at a given depth.
86 /// # Arguments
87 /// * `depth` -Depth the mix is being breathed at.
88 /// * `min` - Minimum tolerable ppO2.
89 /// * `max` - Maximum tolerable ppO2.
90 pub fn in_ppo2_range(&self, depth: usize, min: f64, max: f64) -> bool {
91 let ppo2 = self.pp_o2(depth, 10.0);
92 ppo2 >= min && ppo2 <= max
93 }
94
95 /// Returns the ppO2 of the mix at a given depth.
96 /// # Arguments
97 /// * `depth` - Depth the mix is being breathed at.
98 /// * `metres_per_bar` - Depth of water required to induce 1 bar of pressure.
99 pub fn pp_o2(&self, depth: usize, metres_per_bar: f64) -> f64 {
100 mtr_bar(depth as f64, metres_per_bar) * self.fr_o2()
101 }
102
103 /// Returns the ppHe of the mix at a given depth.
104 /// # Arguments
105 /// * `depth` - Depth the mix is being breathed at.
106 /// * `metres_per_bar` - Depth of water required to induce 1 bar of pressure.
107 pub fn pp_he(&self, depth: usize, metres_per_bar: f64) -> f64 {
108 mtr_bar(depth as f64, metres_per_bar) * self.fr_he()
109 }
110
111 /// Returns the ppN2 of the mix at a given depth.
112 /// # Arguments
113 /// * `depth` - Depth the mix is being breathed at.
114 /// * `metres_per_bar` - Depth of water required to induce 1 bar of pressure.
115 pub fn pp_n2(&self, depth: usize, metre_per_bar: f64) -> f64 {
116 mtr_bar(depth as f64, metre_per_bar) * self.fr_n2()
117 }
118}