astrograph/output/logger/eclipse/
mod.rs1use std::{
2 collections::HashMap,
3 fs::OpenOptions,
4 io::Write,
5 sync::{Arc, RwLock},
6};
7
8use collision_check::CollisionGrid;
9use coordinates::prelude::Spherical;
10
11use crate::{output::Output, Float};
12
13mod collision_check;
15
16#[derive(Clone, Debug, Default)]
17pub struct Logger {
18 eclipse_log: Arc<RwLock<HashMap<Arc<std::path::Path>, Vec<String>>>>,
20}
21
22fn get_eclipses_on_frame(
24 observations: &[(crate::body::Arc, Spherical<Float>)],
25 time: &str,
26) -> Vec<String> {
27 let grid = CollisionGrid::new(observations);
30
31 let mut results = Vec::new();
32
33 for p in observations {
34 let name =
36 p.0.read()
37 .map(|p| p.get_name())
38 .unwrap_or("Poisoned Body".into());
39
40 for (other, magnitude) in grid.collisions(p) {
41 let other_name = other
43 .read()
44 .map(|b| b.get_name())
45 .unwrap_or("Poisoned Body".into());
46
47 results.push(format!("Time={time}, There was an eclipse between {name} and {other_name} with magnitude {magnitude:.2}"));
48 }
49 }
50
51 results
52}
53
54impl Output for Logger {
55 fn write_observations(
56 &self,
57 observations: &[(crate::body::Arc, Spherical<Float>)],
58 observatory_name: &str,
59 time: i128,
60 output_path_root: &std::path::Path,
61 ) -> Result<(), std::io::Error> {
62 let log = get_eclipses_on_frame(observations, &time.to_string());
63 let path = super::super::to_default_path(
64 output_path_root,
65 observatory_name,
66 time,
67 "-eclipses.txt",
68 );
69 if let Ok(mut hash_map) = self.eclipse_log.write() {
70 if let Some(values) = hash_map.get_mut(path.as_path()) {
71 values.extend(log);
72 } else {
73 hash_map.insert(path.into(), log);
74 }
75 }
76
77 Ok(())
78 }
79
80 fn flush(&self) -> Result<(), std::io::Error> {
81 if let Ok(hash_map) = self.eclipse_log.read() {
82 for (path, data) in hash_map.iter() {
83 if let Some(parent) = path.parent() {
85 std::fs::create_dir_all(parent)?;
86 }
87
88 let mut file = OpenOptions::new().create(true).append(true).open(path)?;
90 file.write_all(data.join("\n").as_bytes())?;
92 }
93 }
94 Ok(())
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use coordinates::prelude::Spherical;
102
103 use crate::{body::Body, dynamic::fixed::Fixed};
104
105 #[test]
106 fn eclipse_is_logged_in_correct_format() {
107 let sun = Body::new(None, Fixed::new([0.0, 0.0, 0.0].into()));
108 let earth = Body::new(Some(sun.clone()), Fixed::new([2.0, 0.0, 0.0].into()));
109 let _moon = Body::new(Some(earth.clone()), Fixed::new([-1.0, 0.0, 0.0].into()));
110
111 Body::hydrate_all(&sun, &None);
112 let time = 0.0;
113
114 let observations: Vec<_> = earth
115 .read()
116 .unwrap()
117 .get_observations_from_here(time)
118 .into_iter()
119 .map(|(b, loc)| {
120 let loc = Spherical::from(loc);
121 println!("{loc:?}");
122 (b, loc)
123 })
124 .collect();
125
126 let log = get_eclipses_on_frame(&observations, &time.to_string());
127
128 assert_eq!(
129 log[0],
130 format!(
131 "Time={time}, There was an eclipse between {} and {} with magnitude {:.2}",
132 "0-0", "", 1.0
133 )
134 );
135 }
136}