1pub mod position {
49 use super::common::semi_parameter;
50 use super::common::true_anomaly;
51 use super::coordinate_transforms::euler_angle_transformations;
52 use glam::f32::Vec3;
53 use glam::f64::{DVec2, DVec3};
54
55 pub fn position(parallax: f64, right_ascension: f64, declination: f64) -> DVec3 {
60 let distance = 1. / (parallax / 1000.);
61
62 let distnace_si = distance * (3.0856778570831 * 10_f64.powf(16.));
63
64 let right_ascension_rad = right_ascension.to_radians();
65 let declination_rad = (declination + 90.).to_radians();
66
67 let x = distnace_si * right_ascension_rad.cos() * declination_rad.sin();
68
69 let y = distnace_si * right_ascension_rad.sin() * declination_rad.sin();
70
71 let z = distnace_si * declination_rad.cos();
72
73 DVec3::new(x, y, z)
74 }
75
76 pub fn position_f32(parallax: f32, right_ascension: f32, declination: f32) -> Vec3 {
78 let distance = 1. / (parallax / 1000.);
79
80 let distnace_si = distance * (3.0856778570831 * 10_f32.powf(16.));
81
82 let right_ascension_rad = right_ascension.to_radians();
83 let declination_rad = (declination + 90.).to_radians();
84
85 let x = distnace_si * right_ascension_rad.cos() * declination_rad.sin();
86
87 let y = distnace_si * right_ascension_rad.sin() * declination_rad.sin();
88
89 let z = distnace_si * declination_rad.cos();
90
91 Vec3::new(x, y, z)
92 }
93
94 pub fn position_surface(radius: f64, right_ascension: f64, declination: f64) -> DVec3 {
96 let right_ascension_rad = right_ascension.to_radians();
97 let declination_rad = (declination + 90.).to_radians();
98
99 let x = radius * right_ascension_rad.cos() * declination_rad.sin();
100
101 let y = radius * right_ascension_rad.sin() * declination_rad.sin();
102
103 let z = radius * declination_rad.cos();
104
105 DVec3::new(x, y, z)
106 }
107
108 pub fn companion_position(a: f64, e: f64, period: f64, t_p: f64) -> DVec2 {
113 let p = semi_parameter(a, e);
115 let v = true_anomaly(e, period, t_p);
116
117 let x = (p * v.cos()) / (1. + e * v.cos());
119 let y = (p * v.sin()) / (1. + e * v.cos());
120
121 DVec2::new(x, y)
122 }
123
124 pub fn companion_relative_position(
129 a: f64,
130 e: f64,
131 period: f64,
132 t_p: f64,
133 lotn: f64,
134 aop: f64,
135 i: f64,
136 ) -> DVec3 {
137 let p = semi_parameter(a, e);
139 let v = true_anomaly(e, period, t_p);
140
141 let x = (p * v.cos()) / (1. + e * v.cos());
143 let y = (p * v.sin()) / (1. + e * v.cos());
144
145 let euler_angle_transformations = euler_angle_transformations(lotn, aop, i).to_cols_array();
147 let x1 = euler_angle_transformations[0];
148 let x2 = euler_angle_transformations[1];
149 let x3 = euler_angle_transformations[2];
150
151 let y1 = euler_angle_transformations[3];
152 let y2 = euler_angle_transformations[4];
153 let y3 = euler_angle_transformations[5];
154
155 let companion_position_x = (x1 * x) + (y1 * y);
157 let companion_position_y = (x2 * x) + (y2 * y);
158 let companion_position_z = (x3 * x) + (y3 * y);
159
160 DVec3::new(
161 companion_position_x,
162 companion_position_y,
163 companion_position_z,
164 )
165 }
166}
167
168pub mod velocity {
171 use super::common::radius;
172 use super::common::semi_parameter;
173 use super::common::specific_mechanical_energy;
174 use super::common::standard_gravitational_parameter;
175 use super::common::true_anomaly;
176 use super::coordinate_transforms::euler_angle_transformations;
177 use super::position::position;
178 use glam::f64::{DVec2, DVec3};
179
180 pub fn velocity(
188 parallax: f64,
189 right_ascension: f64,
190 declination: f64,
191 proper_motion_ra: f64,
192 proper_motion_dec: f64,
193 radial_velocity: f64,
194 ) -> DVec3 {
195 let distance = 1. / (parallax / 1000.);
196
197 let distnace_si = distance * (3.0856778570831 * 10_f64.powf(16.));
199 let radial_velocity_si = radial_velocity * 1000.;
200
201 let proper_motion_x = distnace_si
202 * (((right_ascension + ((proper_motion_ra * 0.00027777777777778) / 31556926.))
203 .to_radians())
204 .cos())
205 * ((((declination + ((proper_motion_dec * 0.00027777777777778) / 31556926.)) + 90.)
206 .to_radians())
207 .sin());
208
209 let proper_motion_y = distnace_si
210 * (((right_ascension + ((proper_motion_ra * 0.00027777777777778) / 31556926.))
211 .to_radians())
212 .sin())
213 * ((((declination + ((proper_motion_dec * 0.00027777777777778) / 31556926.)) + 90.)
214 .to_radians())
215 .sin());
216
217 let proper_motion_z = distnace_si
218 * ((((declination + ((proper_motion_dec * 0.00027777777777778) / 31556926.)) + 90.)
219 .to_radians())
220 .cos());
221
222 let position = position(parallax, right_ascension, declination).to_array();
223
224 let x = position[0];
225 let y = position[1];
226 let z = position[2];
227
228 let proper_motion_vector_x = proper_motion_x - x;
229 let proper_motion_vector_y = proper_motion_y - y;
230 let proper_motion_vector_z = proper_motion_z - z;
231
232 let normalized_vector_x = x / (x.powf(2.) + y.powf(2.) + z.powf(2.)).sqrt();
233 let normalized_vector_y = y / (x.powf(2.) + y.powf(2.) + z.powf(2.)).sqrt();
234 let normalized_vector_z = z / (x.powf(2.) + y.powf(2.) + z.powf(2.)).sqrt();
235
236 let radial_velocity_vector_x = normalized_vector_x * radial_velocity_si;
237 let radial_velocity_vector_y = normalized_vector_y * radial_velocity_si;
238 let radial_velocity_vector_z = normalized_vector_z * radial_velocity_si;
239
240 let x_v = radial_velocity_vector_x + proper_motion_vector_x;
241 let y_v = radial_velocity_vector_y + proper_motion_vector_y;
242 let z_v = radial_velocity_vector_z + proper_motion_vector_z;
243
244 DVec3::new(x_v, y_v, z_v)
245 }
246
247 pub fn companion_velocity(a: f64, e: f64, period: f64, t_p: f64) -> DVec2 {
252 let mu = standard_gravitational_parameter(a, period);
254 let p = semi_parameter(a, e);
255 let v = true_anomaly(e, period, t_p);
256
257 let x_v = (0. - ((mu / p).sqrt())) * (v.sin());
259 let y_v = ((mu / p).sqrt()) * (e + (v.cos()));
260
261 DVec2::new(x_v, y_v)
262 }
263
264 pub fn companion_relative_velocity(
269 a: f64,
270 e: f64,
271 period: f64,
272 t_p: f64,
273 lotn: f64,
274 aop: f64,
275 i: f64,
276 ) -> DVec3 {
277 let mu = standard_gravitational_parameter(a, period);
279 let p = semi_parameter(a, e);
280 let v = true_anomaly(e, period, t_p);
281
282 let x_v = (0. - ((mu / p).sqrt())) * (v.sin());
284 let y_v = ((mu / p).sqrt()) * (e + (v.cos()));
285
286 let euler_angle_transformations = euler_angle_transformations(lotn, aop, i).to_cols_array();
288 let x1 = euler_angle_transformations[0];
289 let x2 = euler_angle_transformations[1];
290 let x3 = euler_angle_transformations[2];
291
292 let y1 = euler_angle_transformations[3];
293 let y2 = euler_angle_transformations[4];
294 let y3 = euler_angle_transformations[5];
295
296 let companion_velocity_x = (x1 * x_v) + (y1 * y_v);
298 let companion_velocity_y = (x2 * x_v) + (y2 * y_v);
299 let companion_velocity_z = (x3 * x_v) + (y3 * y_v);
300
301 DVec3::new(
302 companion_velocity_x,
303 companion_velocity_y,
304 companion_velocity_z,
305 )
306 }
307
308 pub fn companion_velocity_value(a: f64, e: f64, period: f64, t_p: f64) -> f64 {
310 let mu = standard_gravitational_parameter(a, period);
311 let epsilon = specific_mechanical_energy(a, e);
312 let r = radius(a, e, period, t_p);
313
314 (2. * ((mu / r) + epsilon)).sqrt()
315 }
316}
317
318pub mod common {
320 use super::coordinate_transforms::euler_angle_transformations;
321 use super::position::companion_relative_position;
322 use super::velocity::companion_relative_velocity;
323 use glam::f64::DVec3;
324
325 pub fn a_to_au(parallax: f64, a: f64) -> f64 {
327 a * parallax_to_parsec(parallax)
328 }
329
330 pub fn declination_total(
332 declination_degree: f64,
333 declination_min: f64,
334 declination_s: f64,
335 ) -> f64 {
336 declination_degree + (declination_min / 60.) + (declination_s / 3600.)
337 }
338
339 pub fn right_ascension_total(
341 right_ascension_h: f64,
342 right_ascension_min: f64,
343 right_ascension_s: f64,
344 ) -> f64 {
345 (right_ascension_h * 15.)
346 + (right_ascension_min * (1. / 4.))
347 + (right_ascension_s * (1. / 240.))
348 }
349
350 pub fn perigee(a: f64, e: f64) -> f64 {
354 a * (1. - e)
355 }
356
357 pub fn apogee(a: f64, e: f64) -> f64 {
361 a * (1. + e)
362 }
363
364 pub fn relative_perigee(a: f64, e: f64, lotn: f64, aop: f64, i: f64) -> DVec3 {
368 let a_si = au_to_m(a);
369 let x = a_si * (1. - e);
370
371 let euler_angle_transformations = euler_angle_transformations(lotn, aop, i).to_cols_array();
372 let x1 = euler_angle_transformations[0];
373 let x2 = euler_angle_transformations[1];
374 let x3 = euler_angle_transformations[2];
375
376 DVec3::new(x * x1, x * x2, x * x3)
377 }
378
379 pub fn relative_apogee(a: f64, e: f64, lotn: f64, aop: f64, i: f64) -> DVec3 {
383 let a_si = au_to_m(a);
384 let x = a_si * (1. + e);
385
386 let euler_angle_transformations = euler_angle_transformations(lotn, aop, i).to_cols_array();
387 let x1 = euler_angle_transformations[0];
388 let x2 = euler_angle_transformations[1];
389 let x3 = euler_angle_transformations[2];
390
391 DVec3::new(x * x1, x * x2, x * x3)
392 }
393
394 pub fn eccentric_anomaly(e: f64, period: f64, t_p: f64) -> f64 {
396 let p_si = period * 31557600.;
398 let t_p_si = t_p * 31557600.;
399
400 let mean_anom = std::f64::consts::PI * 2. * t_p_si / p_si;
402 let mut ecc_anom = mean_anom;
403 for _i in (0..=20).step_by(1) {
404 ecc_anom = mean_anom + (e * ecc_anom.sin());
405 }
406
407 ecc_anom
408 }
409
410 pub fn true_anomaly(e: f64, period: f64, t_p: f64) -> f64 {
412 let p_si = period * 31557600.;
414 let t_p_si = t_p * 31557600.;
415
416 let mean_anom = std::f64::consts::PI * 2. * t_p_si / p_si;
418 let mut ecc_anom = mean_anom;
419 for _i in (0..=20).step_by(1) {
420 ecc_anom = mean_anom + (e * ecc_anom.sin());
421 }
422
423 2. * (((1. + e) / (1. - e)).sqrt() * (ecc_anom * 0.5).tan()).atan()
424 }
425
426 pub fn flight_path_angle(e: f64, period: f64, t_p: f64) -> f64 {
428 let p_si = period * 31557600.;
430 let t_p_si = t_p * 31557600.;
431
432 let mean_anom = std::f64::consts::PI * 2. * t_p_si / p_si;
434 let mut ecc_anom = mean_anom;
435 for _i in (0..=20).step_by(1) {
436 ecc_anom = mean_anom + (e * ecc_anom.sin());
437 }
438
439 ((e * ecc_anom.sin()) / ((1. - ((e.powf(2.)) * (ecc_anom.cos().powf(2.)))).sqrt()))
440 .asin()
441 .to_degrees()
442 }
443
444 pub fn semi_parameter(a: f64, e: f64) -> f64 {
446 let a_si = au_to_m(a);
447 let b_si = semi_minor_axis(a, e);
448
449 (b_si.powf(2.)) / a_si
450 }
451
452 pub fn semi_minor_axis(a: f64, e: f64) -> f64 {
454 let a_si = au_to_m(a);
455
456 a_si * ((1. - e.powf(2.)).sqrt())
457 }
458
459 pub fn radius(a: f64, e: f64, period: f64, t_p: f64) -> f64 {
461 let nu = true_anomaly(e, period, t_p);
462 let p = semi_parameter(a, e);
463
464 p / (1. + (e * nu.cos()))
465 }
466
467 pub fn specific_angular_momentum_value(a: f64, e: f64, period: f64) -> f64 {
469 let p = semi_parameter(a, e);
470 let mu = standard_gravitational_parameter(a, period);
471
472 (mu * p).sqrt()
473 }
474
475 pub fn specific_angular_momentum_coordinates(
477 a: f64,
478 e: f64,
479 period: f64,
480 t_p: f64,
481 lotn: f64,
482 aop: f64,
483 i: f64,
484 ) -> DVec3 {
485 let r = companion_relative_position(a, e, period, t_p, lotn, aop, i);
486 let v = companion_relative_velocity(a, e, period, t_p, lotn, aop, i);
487
488 DVec3::cross(r, v)
489 }
490
491 pub fn standard_gravitational_parameter(a: f64, period: f64) -> f64 {
493 let period_si = period * 31557600.;
494 let a_si = au_to_m(a);
495
496 ((a_si.powf(3.)) * 4. * (std::f64::consts::PI.powf(2.))) / (period_si.powf(2.))
497 }
498
499 pub fn specific_mechanical_energy(a: f64, period: f64) -> f64 {
501 let a_si = au_to_m(a);
502 let mu = standard_gravitational_parameter(a, period);
503
504 0. - (mu / (2. * a_si))
505 }
506
507 pub fn period(a: f64, mu: f64) -> f64 {
509 let a_si = au_to_m(a);
510
511 2. * std::f64::consts::PI * (((a_si.powf(3.)) / mu).sqrt())
512 }
513
514 pub fn semi_major_axis(
516 standard_gravitational_parameter: f64,
517 specific_mechanical_energy: f64,
518 ) -> f64 {
519 0. - (standard_gravitational_parameter / (2. * specific_mechanical_energy))
520 }
521
522 pub fn mean_motion(a: f64, period: f64) -> f64 {
524 let a_si = au_to_m(a);
525 let mu = standard_gravitational_parameter(a, period);
526
527 (mu / (a_si.powf(3.))).sqrt()
528 }
529
530 pub fn eccentricity(
532 standard_gravitational_parameter: f64,
533 specific_mechanical_energy: f64,
534 specific_angular_momentum_value: f64,
535 ) -> f64 {
536 (1. - ((2. * specific_mechanical_energy * (specific_angular_momentum_value.powf(2.)))
537 / (standard_gravitational_parameter.powf(2.))))
538 .sqrt()
539 }
540
541 pub fn linear_eccentricity(a: f64, e: f64) -> f64 {
543 a * e
544 }
545
546 pub fn flattening(a: f64, e: f64) -> f64 {
548 let a_si = au_to_m(a);
549 let b = semi_minor_axis(a_si, e);
550
551 (a_si - b) / a_si
552 }
553
554 pub fn parallax_to_parsec(parallax: f64) -> f64 {
556 1. / (parallax / 1000.)
557 }
558
559 pub fn au_to_m(a: f64) -> f64 {
561 a * 1000. * 149597870.7
562 }
563
564 pub fn apparent_mag_to_absolute_mag(parallax: f64, apparent_magnitude: f64) -> f64 {
566 apparent_magnitude + 5. * (parallax.log10() + 1.)
567 }
568
569 pub fn temperature(b_v_index: f64) -> f64 {
571 4600. * ((1. / ((0.92 * b_v_index) + 1.7)) + (1. / ((0.92 * b_v_index) + 0.62)))
572 }
573}
574
575pub mod coordinate_transforms {
577 use glam::f64::{DMat3, DVec3};
578
579 pub fn euler_angle_transformations(lotn: f64, aop: f64, i: f64) -> DMat3 {
584 let lotn_rad = lotn.to_radians();
586 let aop_rad = aop.to_radians();
587 let i_rad = i.to_radians();
588
589 let x1 = (lotn_rad.cos() * aop_rad.cos()) - (lotn_rad.sin() * i_rad.cos() * aop_rad.sin());
591 let x2 = (lotn_rad.sin() * aop_rad.cos()) + (lotn_rad.cos() * i_rad.cos() * aop_rad.sin());
592 let x3 = i_rad.sin() * aop_rad.sin();
593
594 let y1 = ((0. - lotn_rad.cos()) * aop_rad.sin())
595 - (lotn_rad.sin() * i_rad.cos() * aop_rad.cos());
596 let y2 = ((0. - lotn_rad.sin()) * aop_rad.sin())
597 + (lotn_rad.cos() * i_rad.cos() * aop_rad.cos());
598 let y3 = i_rad.sin() * aop_rad.cos();
599
600 let z1 = i_rad.sin() * lotn_rad.sin();
601 let z2 = (0. - i_rad.sin()) * lotn_rad.cos();
602 let z3 = i_rad.cos();
603
604 DMat3::from_cols(
605 DVec3::new(x1, x2, x3),
606 DVec3::new(y1, y2, y3),
607 DVec3::new(z1, z2, z3),
608 )
609 }
610}
611
612pub mod input_data {
616 use csv::{ReaderBuilder, Terminator};
617 use std::error::Error;
618
619 pub fn parse_csv<T: for<'de> serde::Deserialize<'de>>(
621 filename: &str,
622 has_headers: bool,
623 cols_split: u8,
624 row_split: u8,
625 ) -> Result<std::vec::Vec<T>, Box<dyn Error>> {
626 let mut vec = vec![];
627 let mut rdr = ReaderBuilder::new()
628 .delimiter(cols_split)
629 .terminator(Terminator::Any(row_split))
630 .has_headers(has_headers)
631 .from_path(filename)?;
632 for result in rdr.deserialize() {
633 let record: T = result?;
634 vec.push(record);
635 }
636 Ok(vec)
637 }
638}
639
640pub mod output_data {
642 use csv::{Terminator, WriterBuilder};
643 use std::error::Error;
644
645 pub fn write_csv<T: serde::Serialize>(
647 output_filename: &str,
648 has_headers: bool,
649 cols_split: u8,
650 row_split: u8,
651 vec: std::vec::Vec<T>,
652 ) -> Result<(), Box<dyn Error>> {
653 let mut writer = WriterBuilder::new()
654 .delimiter(cols_split)
655 .terminator(Terminator::Any(row_split))
656 .has_headers(has_headers)
657 .from_path(output_filename)?;
658
659 for n in vec {
660 writer.serialize(n)?;
661 }
662
663 Ok(())
664 }
665}
666
667pub mod nbss {
669 use serde::Deserialize;
670 use serde::Serialize;
671
672 #[derive(Debug, Deserialize)]
674 #[serde(rename_all = "PascalCase")]
675 pub struct NBSSInputCollums {
676 pub name: String,
677 pub mass: f64,
678 pub radius: f64,
679 pub reference_body: String,
680 pub a: f64,
681 pub e: f64,
682 pub period: f64,
683 pub time_since_periapsis: f64,
684 pub lotn: f64,
685 pub aop: f64,
686 pub i: f64,
687 }
688
689 #[derive(Serialize)]
691 #[serde(rename_all = "PascalCase")]
692 pub struct NBSSOutputCollums {
693 pub name: String,
694 pub mass: f64,
695 pub radius: f64,
696 pub reference_body: String,
697 pub e: f64,
698 pub semi_parameter: f64,
699 pub position_x: f64,
700 pub position_y: f64,
701 pub position_z: f64,
702 pub velocity_x: f64,
703 pub velocity_y: f64,
704 pub velocity_z: f64,
705 }
706
707 pub fn position_and_velocity_twobody_serialized(input_filename: &str, output_filename: &str) {
709 let mut file: std::vec::Vec<NBSSInputCollums> = vec![];
710 match super::input_data::parse_csv(input_filename, false, b',', b'\n') {
711 Ok(vec) => file = vec,
712 Err(ex) => {
713 println!("ERROR -> {}", ex);
714 }
715 };
716
717 let mut vec = vec![];
718
719 for n in file {
720 if n.a == 0. && n.e == 0. {
721 let row = NBSSOutputCollums {
722 name: n.name,
723 mass: n.mass,
724 radius: n.radius,
725 reference_body: n.reference_body,
726 e: 0.,
727 semi_parameter: 0.,
728 position_x: 0.,
729 position_y: 0.,
730 position_z: 0.,
731 velocity_x: 0.,
732 velocity_y: 0.,
733 velocity_z: 0.,
734 };
735
736 vec.push(row);
737 } else {
738 let pos = super::position::companion_relative_position(
739 n.a,
740 n.e,
741 n.period,
742 n.time_since_periapsis,
743 n.lotn,
744 n.aop,
745 n.i,
746 )
747 .to_array();
748 let vel = super::velocity::companion_relative_velocity(
749 n.a,
750 n.e,
751 n.period,
752 n.time_since_periapsis,
753 n.lotn,
754 n.aop,
755 n.i,
756 )
757 .to_array();
758 let semi_parameter = super::common::semi_parameter(n.a, n.e);
759
760 let row = NBSSOutputCollums {
761 name: n.name,
762 mass: n.mass,
763 radius: n.radius,
764 reference_body: n.reference_body,
765 e: n.e,
766 semi_parameter,
767 position_x: pos[0],
768 position_y: pos[1],
769 position_z: pos[2],
770 velocity_x: vel[0],
771 velocity_y: vel[1],
772 velocity_z: vel[2],
773 };
774
775 vec.push(row);
776 }
777 }
778
779 match super::output_data::write_csv(output_filename, false, b',', b'\n', vec) {
780 Ok(_) => (),
781 Err(ex) => {
782 println!("ERROR -> {}", ex);
783 }
784 };
785 }
786
787 }
798
799