Advertisement
Guest User

Untitled

a guest
Mar 23rd, 2017
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.78 KB | None | 0 0
  1. #![allow(dead_code)]
  2. extern crate ansi_term;
  3.  
  4. mod engines;
  5. mod fuels;
  6.  
  7. use std::fmt;
  8. use std::marker::PhantomData;
  9. use ansi_term::Colour::{Red, Yellow, Blue};
  10. use self::engines::*;
  11.  
  12. #[allow(unused_variables, unused_mut)]
  13. fn main() {
  14. // Probes
  15. let probe_200kg = SimpleStage { dry_mass: 138.0, engines: vec![THRUSTER.with_burn_time(108.0)] };
  16. let geo_probe_200kg = SimpleStage { dry_mass: 156.0, engines: vec![THRUSTER.with_burn_time(78.0)] };
  17. let interplanetary_probe = SimpleStage { dry_mass: 195.0, engines: vec![THRUSTER.with_burn_time(185.0)] };
  18. let geo_probe_2 = SimpleStage { dry_mass: 237.0, engines: vec![THRUSTER_2.with_burn_time(95.5)] };
  19.  
  20. // Upper stages
  21. let ablestar = SimpleStage { dry_mass: 667.0, engines: vec![AJ10_104D] };
  22. let agena_a = SimpleStage { dry_mass: 640.0, engines: vec![BELL_8048] };
  23. let agena_b = SimpleStage { dry_mass: 675.0, engines: vec![BELL_8081] };
  24. let agena_d = SimpleStage { dry_mass: 730.0, engines: vec![BELL_8247] };
  25. let baby_sergeant_1 = SimpleStage { dry_mass: BABY_SERGEANT.mass, engines: vec![BABY_SERGEANT] };
  26. let baby_sergeant_3 = SimpleStage { dry_mass: BABY_SERGEANT.mass * 3.0, engines: vec![BABY_SERGEANT; 3] };
  27. let baby_sergeant_11 = SimpleStage { dry_mass: BABY_SERGEANT.mass * 11.0, engines: vec![BABY_SERGEANT; 11] };
  28.  
  29. // Lower stages
  30. let atlas_vernier = LR101_NA_3.with_burn_time(LR105_NA_3.burn_time);
  31. let atlas_a = BoostedStage {
  32. core: SimpleStage { dry_mass: 5400.0, engines: vec![LR105_NA_3, atlas_vernier, atlas_vernier] },
  33. booster: SimpleStage { dry_mass: LR89_NA_3.mass, engines: vec![LR89_NA_3] },
  34. booster_count: 2,
  35. };
  36. let atlas_b = BoostedStage {
  37. core: SimpleStage { dry_mass: 3020.0, engines: vec![LR105_NA_5, atlas_vernier, atlas_vernier] },
  38. booster: SimpleStage { dry_mass: LR89_NA_5.mass, engines: vec![LR89_NA_5] },
  39. booster_count: 2,
  40. };
  41. let atlas_vernier_2 = LR101_NA_11.with_burn_time(LR105_NA_5.burn_time);
  42. let atlas_c = BoostedStage {
  43. core: SimpleStage { dry_mass: 3920.0, engines: vec![LR105_NA_5, atlas_vernier_2, atlas_vernier_2] },
  44. booster: SimpleStage { dry_mass: LR89_NA_7_1.mass, engines: vec![LR89_NA_7_1] },
  45. booster_count: 2,
  46. };
  47. let saturn_i = SimpleStage { dry_mass: 21150.0, engines: vec![H1; 8] };
  48.  
  49. let mut rocket = Rocket {
  50. stages: vec![Box::new(atlas_c), Box::new(agena_d), Box::new(geo_probe_2)],
  51. payload_mass: 0.0,
  52. };
  53.  
  54. // print_max_payloads(&mut rocket);
  55. // rocket.set_payload_for_target_deltav(DV_TO_ORBIT);
  56.  
  57. println!("{:5} {:>10} {:>10} {:>10} {:>10}", "stage", "delta-v", "wet mass", "dry mass", "burn time");
  58. let reversed_stages = rocket.stages().enumerate().collect::<Vec<_>>().into_iter().rev();
  59. for (i, stage) in reversed_stages {
  60. println!("{:5}: {:10.0} {:10.0} {:10.0} {:>10}", i, stage.delta_v(), stage.wet_mass(), stage.dry_mass(), BurnTime(stage.burn_time()));
  61. }
  62. println!("Total: {:10.0}", rocket.delta_v());
  63. println!("Max G Force Endured: {}", rocket.max_g_force());
  64. print_where_rocket_can_go(&rocket);
  65. }
  66.  
  67. const GRAVITY: f64 = 9.82;
  68. const DV_TO_ORBIT: f64 = 9200.0;
  69. const DV_TO_GTO: f64 = DV_TO_ORBIT + 2440.0;
  70. const DV_TO_GEO: f64 = DV_TO_GTO + 1850.0;
  71. const DV_TO_TLI: f64 = DV_TO_GTO + 680.0;
  72. const DV_TO_LLO: f64 = DV_TO_TLI + 140.0 + 680.0;
  73. const DV_TO_VENUS: f64 = DV_TO_TLI + 370.0;
  74. const DV_TO_MARS: f64 = DV_TO_TLI + 480.0;
  75. const DV_TO_MERCURY: f64 = DV_TO_VENUS + 2060.0;
  76.  
  77. trait Stage {
  78. fn engines(&self) -> Vec<Engine>;
  79. fn dry_mass(&self) -> f64;
  80. fn wet_mass(&self) -> f64;
  81.  
  82. fn burn_time(&self) -> f64 {
  83. self.engines().iter().map(|e| e.burn_time)
  84. .max_by_key(|x| *x as u64).unwrap_or(0.0)
  85. }
  86.  
  87. fn isp(&self) -> f64 {
  88. let engines = self.engines();
  89. engines.iter().map(|e| e.thrust).sum::<f64>() /
  90. engines.iter().map(|e| e.thrust / e.isp).sum::<f64>()
  91. }
  92.  
  93. /// Simple stages don't need to implement this method. It is used to
  94. /// calculate delta-v when there are boosters involved. To combine multiple
  95. /// simple stages, use `Rocket` instead.
  96. fn next_stage(&self) -> Option<Box<Stage>> {
  97. None
  98. }
  99.  
  100. fn delta_v(&self) -> f64 {
  101. self.isp() * (self.wet_mass() / self.dry_mass()).ln() * GRAVITY
  102. }
  103.  
  104. fn max_g_force(&self) -> f64 {
  105. self.engines().iter().map(|e| e.thrust * 1000.0).sum::<f64>() /
  106. self.dry_mass() / GRAVITY
  107. }
  108. }
  109.  
  110. impl<T: ?Sized + Stage> Stage for Box<T> {
  111. fn engines(&self) -> Vec<Engine> { (&**self).engines() }
  112. fn dry_mass(&self) -> f64 { (&**self).dry_mass() }
  113. fn wet_mass(&self) -> f64 { (&**self).wet_mass() }
  114. fn burn_time(&self) -> f64 { (&**self).burn_time() }
  115. fn next_stage(&self) -> Option<Box<Stage>> { (&**self).next_stage() }
  116. fn delta_v(&self) -> f64 { (&**self).delta_v() }
  117. }
  118.  
  119. impl<'a, T: ?Sized + Stage> Stage for &'a T {
  120. fn engines(&self) -> Vec<Engine> { (&**self).engines() }
  121. fn dry_mass(&self) -> f64 { (&**self).dry_mass() }
  122. fn wet_mass(&self) -> f64 { (&**self).wet_mass() }
  123. fn burn_time(&self) -> f64 { (&**self).burn_time() }
  124. fn next_stage(&self) -> Option<Box<Stage>> { (&**self).next_stage() }
  125. fn delta_v(&self) -> f64 { (&**self).delta_v() }
  126. }
  127.  
  128. #[derive(Debug, Clone)]
  129. struct SimpleStage {
  130. dry_mass: f64,
  131. engines: Vec<Engine>,
  132. }
  133.  
  134. impl SimpleStage {
  135. fn with_remaining_burn_time(&self, burn_time: f64) -> Self {
  136. let mut new_stage = self.clone();
  137. for engine in &mut new_stage.engines {
  138. engine.burn_time = burn_time;
  139. }
  140. new_stage
  141. }
  142. }
  143.  
  144. impl Stage for SimpleStage {
  145. fn engines(&self) -> Vec<Engine> { self.engines.clone() }
  146. fn dry_mass(&self) -> f64 { self.dry_mass }
  147.  
  148. fn wet_mass(&self) -> f64 {
  149. self.dry_mass + self.engines.iter().map(|e| e.propellant_mass_for_full_burn()).sum::<f64>()
  150. }
  151. }
  152.  
  153. #[derive(Debug, Clone)]
  154. struct BoostedStage {
  155. core: SimpleStage,
  156. booster: SimpleStage,
  157. booster_count: usize,
  158. }
  159.  
  160. impl BoostedStage {
  161. fn stage_after_booster_separation(&self) -> SimpleStage {
  162. self.core.with_remaining_burn_time(self.core.burn_time() - self.booster.burn_time())
  163. }
  164. }
  165.  
  166. impl Stage for BoostedStage {
  167. fn engines(&self) -> Vec<Engine> {
  168. let mut engines = self.core.engines();
  169. let booster_engines = self.booster.engines();
  170. for _ in 0..self.booster_count {
  171. engines.extend_from_slice(&booster_engines);
  172. }
  173. engines
  174. }
  175.  
  176. fn burn_time(&self) -> f64 {
  177. self.booster.burn_time()
  178. }
  179.  
  180. fn dry_mass(&self) -> f64 {
  181. self.stage_after_booster_separation().wet_mass() + self.booster.dry_mass() * self.booster_count as f64
  182. }
  183.  
  184. fn wet_mass(&self) -> f64 {
  185. self.core.wet_mass() + self.booster.wet_mass() * self.booster_count as f64
  186. }
  187.  
  188. fn next_stage(&self) -> Option<Box<Stage>> {
  189. Some(Box::new(self.stage_after_booster_separation()))
  190. }
  191. }
  192.  
  193. struct StageWithPayload<T> {
  194. stage: T,
  195. payload_mass: f64,
  196. }
  197.  
  198. impl<T: Stage> Stage for StageWithPayload<T> {
  199. fn engines(&self) -> Vec<Engine> {
  200. self.stage.engines()
  201. }
  202.  
  203. fn dry_mass(&self) -> f64 {
  204. self.stage.dry_mass() + self.payload_mass
  205. }
  206.  
  207. fn wet_mass(&self) -> f64 {
  208. self.stage.wet_mass() + self.payload_mass
  209. }
  210.  
  211. fn next_stage(&self) -> Option<Box<Stage>> {
  212. self.stage.next_stage().map(|s| Box::new(StageWithPayload {
  213. stage: s,
  214. payload_mass: self.payload_mass,
  215. }) as Box<Stage>)
  216. }
  217.  
  218. fn burn_time(&self) -> f64 {
  219. self.stage.burn_time()
  220. }
  221. }
  222.  
  223. struct Rocket {
  224. stages: Vec<Box<Stage>>,
  225. payload_mass: f64,
  226. }
  227.  
  228. impl Rocket {
  229. fn stages<'a>(&'a self) -> Box<Iterator<Item=Box<Stage + 'a>> + 'a> {
  230. let mut iterator = RocketStages {
  231. current: None,
  232. remaining: self.stages.iter(),
  233. payload_mass: self.payload_mass,
  234. _marker: PhantomData,
  235. };
  236. iterator.next();
  237. Box::new(iterator)
  238. }
  239.  
  240. fn delta_v(&self) -> f64 {
  241. self.stages().map(|s| s.delta_v()).sum()
  242. }
  243.  
  244. fn set_payload_for_target_deltav(&mut self, target_delta_v: f64) {
  245. self.payload_mass = 0.0;
  246. let mut last_mass = 0.0;
  247. while self.delta_v() > target_delta_v {
  248. last_mass = self.payload_mass;
  249. if self.payload_mass < 50.0 {
  250. self.payload_mass += 10.0;
  251. } else if self.payload_mass < 500.0 {
  252. self.payload_mass += 50.0;
  253. } else {
  254. self.payload_mass += 100.0;
  255. }
  256. }
  257. self.payload_mass = last_mass;
  258. }
  259.  
  260. fn max_g_force(&self) -> f64 {
  261. let mut g_forces = self.stages().map(|s| s.max_g_force()).collect::<Vec<_>>();
  262. g_forces.sort_by(|a, b| b.partial_cmp(a).expect("We should never get NaN here"));
  263. *g_forces.first().unwrap_or(&0.0)
  264. }
  265. }
  266.  
  267. struct RocketStages<'a, T> {
  268. current: Option<Box<Stage + 'a>>,
  269. remaining: T,
  270. payload_mass: f64,
  271. _marker: PhantomData<&'a ()>
  272. }
  273.  
  274. impl<'a, T, U> Iterator for RocketStages<'a, U> where
  275. T: Stage + 'a,
  276. U: Iterator<Item=T> + Clone,
  277. {
  278. type Item = Box<Stage + 'a>;
  279.  
  280. fn next(&mut self) -> Option<Self::Item> {
  281. let result = self.current.take();
  282. self.current = result.as_ref().and_then(|s| s.next_stage())
  283. .or_else(|| {
  284. self.remaining.next().map(|s| {
  285. let upper_stage_weight = self.remaining.clone().map(|s| s.wet_mass()).sum::<f64>();
  286. Box::new(StageWithPayload {
  287. stage: s,
  288. payload_mass: upper_stage_weight + self.payload_mass,
  289. }) as Box<Stage>
  290. })
  291. });
  292. result
  293. }
  294. }
  295.  
  296. struct BurnTime(f64);
  297.  
  298. impl fmt::Display for BurnTime {
  299. fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
  300. let minutes = self.0 as u64 / 60;
  301. let seconds = self.0 as u64 % 60;
  302. if seconds == 0 {
  303. format!("{}m", minutes).fmt(fmt)
  304. } else if minutes == 0 {
  305. format!("{}s", seconds).fmt(fmt)
  306. } else {
  307. format!("{}m {}s", minutes, seconds).fmt(fmt)
  308. }
  309. }
  310. }
  311.  
  312. fn print_where_rocket_can_go(rocket: &Rocket) {
  313. let dv = rocket.delta_v();
  314. if dv <= DV_TO_ORBIT {
  315. println!("{}", Red.bold().paint("This rocket will not reach orbit"));
  316. }
  317.  
  318. print_if_rocket_can_go_to(dv, DV_TO_GTO, "GTO");
  319. print_if_rocket_can_go_to(dv, DV_TO_GEO, "GEO");
  320. print_if_rocket_can_go_to(dv, DV_TO_TLI, "The Moon");
  321. print_if_rocket_can_go_to(dv, DV_TO_LLO, "Lunar Orbit");
  322. print_if_rocket_can_go_to(dv, DV_TO_VENUS, "Venus");
  323. print_if_rocket_can_go_to(dv, DV_TO_MARS, "Mars");
  324. print_if_rocket_can_go_to(dv, DV_TO_MERCURY, "Mercury");
  325. if dv > DV_TO_GTO {
  326. println!("Note: Assumes no gravity assists");
  327. }
  328. }
  329.  
  330. fn print_if_rocket_can_go_to(dv: f64, required_dv: f64, name: &str) {
  331. if dv > required_dv * 1.05 {
  332. let excess = dv - required_dv;
  333. println!("{}", Blue.paint(format!("This rocket can go to {} with {:.0} m/s excess dV", name, excess)));
  334. } else if dv > required_dv {
  335. println!("{}", Yellow.paint(format!("This rocket can go to {} without safety margins", name)));
  336. }
  337. }
  338.  
  339. fn print_max_payloads(rocket: &mut Rocket) {
  340. print_max_payoad(rocket, DV_TO_ORBIT, "orbit");
  341. print_max_payoad(rocket, DV_TO_GTO * 1.05, "GTO");
  342. print_max_payoad(rocket, DV_TO_GEO * 1.05, "GEO");
  343. print_max_payoad(rocket, DV_TO_TLI * 1.05, "TLI");
  344. print_max_payoad(rocket, DV_TO_LLO * 1.05, "Lunar Orbit");
  345. print_max_payoad(rocket, DV_TO_VENUS * 1.05, "Venus");
  346. print_max_payoad(rocket, DV_TO_MERCURY * 1.05, "Mercury");
  347. }
  348.  
  349. fn print_max_payoad(rocket: &mut Rocket, required_dv: f64, name: &str) {
  350. rocket.set_payload_for_target_deltav(required_dv);
  351. if rocket.payload_mass > 0.0 {
  352. println!("Max to {}: {}", name, rocket.payload_mass);
  353. }
  354. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement