qsm_core/utils/
echo_time.rs1#[cfg_attr(feature = "introspection", derive(serde::Serialize))]
14#[derive(Clone, Copy, Debug, PartialEq)]
15pub enum EchoTime {
16 First,
18 Last,
20 Spacing,
23 Sum,
26 Seconds(f64),
28}
29
30impl EchoTime {
31 pub fn resolve(self, echo_times: &[f64]) -> Result<f64, String> {
37 let te = match self {
38 EchoTime::Seconds(te) => te,
39 _ if echo_times.is_empty() => {
40 return Err(format!("the {} needs the echo times; none were given", self.describe()));
41 }
42 EchoTime::First => echo_times[0],
43 EchoTime::Last => echo_times[echo_times.len() - 1],
44 EchoTime::Spacing if echo_times.len() == 1 => echo_times[0],
45 EchoTime::Spacing => echo_times[1] - echo_times[0],
46 EchoTime::Sum => echo_times.iter().sum(),
47 };
48 if te.is_finite() && te > 0.0 {
49 Ok(te)
50 } else {
51 Err(format!(
52 "the {} must be a finite time > 0 s, got {te} s (echo times {echo_times:?})",
53 self.describe()
54 ))
55 }
56 }
57
58 pub(crate) fn seconds(self, echo_times: &[f64], method: &str) -> f64 {
61 self.resolve(echo_times).unwrap_or_else(|e| panic!("{method}: {e}"))
62 }
63
64 fn describe(self) -> &'static str {
65 match self {
66 EchoTime::First => "first echo time",
67 EchoTime::Last => "last echo time",
68 EchoTime::Spacing => "echo spacing",
69 EchoTime::Sum => "sum of the echo times",
70 EchoTime::Seconds(_) => "echo time",
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn picks_each_choice() {
81 let te = [0.004, 0.012, 0.020];
82 assert_eq!(EchoTime::First.resolve(&te), Ok(0.004));
83 assert_eq!(EchoTime::Last.resolve(&te), Ok(0.020));
84 assert!((EchoTime::Spacing.resolve(&te).unwrap() - 0.008).abs() < 1e-15);
85 assert!((EchoTime::Sum.resolve(&te).unwrap() - 0.036).abs() < 1e-15);
86 assert_eq!(EchoTime::Seconds(0.03).resolve(&te), Ok(0.03));
87 }
88
89 #[test]
90 fn one_echo_is_that_echo_for_every_scan_choice() {
91 for c in [EchoTime::First, EchoTime::Last, EchoTime::Spacing, EchoTime::Sum] {
92 assert_eq!(c.resolve(&[0.02]), Ok(0.02), "{c:?}");
93 }
94 }
95
96 #[test]
97 fn refuses_what_it_cannot_resolve() {
98 assert!(EchoTime::First.resolve(&[]).unwrap_err().contains("needs the echo times"));
99 assert_eq!(EchoTime::Seconds(0.01).resolve(&[]), Ok(0.01));
100 assert!(EchoTime::Spacing.resolve(&[0.02, 0.01]).is_err());
101 assert!(EchoTime::Seconds(0.0).resolve(&[0.01]).is_err());
102 assert!(EchoTime::Seconds(f64::NAN).resolve(&[0.01]).is_err());
103 }
104}