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}