├── .gitignore ├── src ├── main.rs ├── doppler_factor.rs ├── propogation │ ├── mod.rs │ ├── gstime.rs │ ├── initl.rs │ ├── dspace.rs │ ├── sgp4.rs │ ├── dpper.rs │ ├── dscom.rs │ ├── dsinit.rs │ └── sgp4init.rs ├── constants.rs ├── ext.rs ├── transforms.rs ├── lib.rs └── io.rs ├── .travis.yml ├── Cargo.toml ├── .github └── workflows │ └── rust.yml ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("hello!"); 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | matrix: 7 | allow_failures: 8 | - rust: nightly 9 | fast_finish: true -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "satellite" 3 | version = "0.1.0" 4 | authors = ["Rich Infante "] 5 | license = "MIT" 6 | repository = "https://github.com/richinfante/satellite-rs" 7 | readme = "README.md" 8 | edition = "2018" 9 | 10 | [dependencies] 11 | chrono = { version = "0.4" } -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Build 13 | run: cargo build --verbose 14 | - name: Run tests 15 | run: cargo test --verbose 16 | -------------------------------------------------------------------------------- /src/doppler_factor.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | use crate::Vec3; 3 | 4 | fn sign(val: f64) -> f64 { 5 | if val >= 0.0 { 6 | 1.0 7 | } else { 8 | -1.0 9 | } 10 | } 11 | 12 | pub fn doppler_factor(location: Vec3, position: Vec3, velocity: Vec3) -> f64 { 13 | let current_range = position.range(&location); 14 | 15 | let next_pos = position.add(&velocity); 16 | 17 | let next_range = next_pos.range(&location); 18 | 19 | let mut range_rate = next_range - current_range; 20 | 21 | // TODO: is this simply abs() 22 | range_rate *= sign(range_rate); 23 | 24 | return 1.0 + (range_rate / C); 25 | } 26 | -------------------------------------------------------------------------------- /src/propogation/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod dpper; 2 | pub mod dscom; 3 | pub mod dsinit; 4 | pub mod dspace; 5 | pub mod gstime; 6 | pub mod initl; 7 | pub mod sgp4; 8 | pub mod sgp4init; 9 | 10 | 11 | use crate::constants::*; 12 | use crate::io::*; 13 | use crate::ext::*; 14 | use crate::propogation::sgp4::*; 15 | 16 | use chrono::prelude::*; 17 | use chrono::DateTime; 18 | 19 | /// Propogate changes to a satrec for a specific datetime. 20 | pub fn propogate_datetime(satrec: &Satrec, time: DateTime) -> Result { 21 | let j = jday( 22 | time.year() as f64, 23 | time.month() as f64, 24 | time.day() as f64, 25 | time.hour() as f64, 26 | time.minute() as f64, 27 | time.second() as f64, 28 | 0 as f64 29 | ); 30 | 31 | let m = (j - satrec.jdsatepoch) * MINUTES_PER_DAY; 32 | let mut rec = satrec.clone(); 33 | return sgp4(&mut rec, m); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019 Richard Infante 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/propogation/gstime.rs: -------------------------------------------------------------------------------- 1 | use crate::ext; 2 | use crate::constants::*; 3 | use chrono::prelude::*; 4 | 5 | /// Convert a julian date to GMST (Greenwich Mean Sidereal Time) 6 | pub fn gstime(jdut1: f64) -> f64 { 7 | let tut1 = (jdut1 - 2451545.0) / 36525.0; 8 | 9 | let mut temp = (-6.2e-6 * tut1 * tut1 * tut1) 10 | + (0.093104 * tut1 * tut1) 11 | + (((876600.0 * 3600.0) + 8640184.812866) * tut1) 12 | + 67310.54841; // # sec 13 | temp = ((temp * DEG_2_RAD) / 240.0) % TWO_PI; // 360/86400 = 1/240, to deg, to rad 14 | 15 | // ------------------------ check quadrants --------------------- 16 | if temp < 0.0 { 17 | temp += TWO_PI; 18 | } 19 | 20 | return temp; 21 | } 22 | 23 | /// Convert a datetime to GMST (Greenwich Mean Sidereal Time) 24 | pub fn gstime_datetime(datetime: DateTime) -> f64 { 25 | let jday = ext::jday_datetime(datetime); 26 | return gstime(jday) 27 | } 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | use crate::propogation::gstime::*; 32 | #[test] 33 | fn test_gst() { 34 | let res = gstime(2444468.79629788); 35 | assert_eq!(res, 1.265125075734467); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | pub use std::f64::consts::PI; 2 | 3 | /// 2 * π 4 | pub static TWO_PI: f64 = PI * 2.0; 5 | 6 | /// Conversion factor for Degrees -> Radians 7 | pub static DEG_2_RAD: f64 = PI / 180.0; 8 | 9 | /// Conversion factorfor Radians -> Degrees 10 | pub static RAD_TO_DEG: f64 = 180.0 / PI; 11 | 12 | /// Minutes per day constant. 13 | pub static MINUTES_PER_DAY: f64 = 1440.0; 14 | 15 | pub static MU: f64 = 398600.5; // in km3 / s2 16 | 17 | /// Earth's Radiys (km) 18 | pub static EARTH_RADIUS: f64 = 6378.137; 19 | 20 | // Two-thirds precomputed constant. 21 | pub static X2O3: f64 = 2.0 / 3.0; 22 | 23 | /// Speed of light in km/s 24 | pub static C: f64 = 299792.458; 25 | 26 | /// Km to mi conversion 27 | pub static KM_TO_MI: f64 = 0.62137; 28 | 29 | // Mi to Km conversion 30 | pub static MI_TO_KM: f64 = 1.0 / 0.62137; 31 | 32 | pub static J2: f64 = 0.00108262998905; 33 | pub static J3: f64 = -0.00000253215306; 34 | pub static J4: f64 = -0.00000161098761; 35 | pub static J3OJ2: f64 = J3 / J2; 36 | pub static XKE : f64 = 0.07436685316871385; 37 | pub static TUMIN : f64 = 13.446851082044981; 38 | pub static XPDOTP: f64 = 1440.0 / (2.0 * std::f64::consts::PI); 39 | 40 | #[cfg(test)] 41 | mod test { 42 | use crate::constants::*; 43 | 44 | pub fn xke() -> f64 { 45 | 60.0 / ((EARTH_RADIUS * EARTH_RADIUS * EARTH_RADIUS) / MU).sqrt() 46 | } 47 | 48 | pub fn tumin() -> f64 { 49 | 1.0 / xke() 50 | } 51 | 52 | #[test] 53 | fn test() { 54 | assert_eq!(PI, 3.141592653589793); 55 | assert_eq!(TWO_PI, 6.283185307179586); 56 | assert_eq!(DEG_2_RAD, 0.017453292519943295); 57 | assert_eq!(RAD_TO_DEG, 57.29577951308232); 58 | assert_eq!(MINUTES_PER_DAY, 1440.0); 59 | assert_eq!(MU, 398600.5); 60 | assert_eq!(EARTH_RADIUS, 6378.137); 61 | assert_eq!(XKE, xke()); 62 | assert_eq!(TUMIN, tumin()); 63 | assert_eq!(xke(), 0.07436685316871385); 64 | assert_eq!(tumin(), 13.446851082044981); 65 | assert_eq!(J2, 0.00108262998905); 66 | assert_eq!(J3, -0.00000253215306); 67 | assert_eq!(J4, -0.00000161098761); 68 | assert_eq!(J3OJ2, -0.0023388905587420003); 69 | assert_eq!(X2O3, 0.6666666666666666); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # satellite-rs 2 | ![build status](https://api.travis-ci.org/richinfante/satellite-rs.svg?branch=master) 3 | 4 | A port of [satellite-js](https://github.com/shashwatak/satellite-js) to rust. 5 | 6 | Currently unstable, and unsuitable for production use. 7 | 8 | 9 | ### Cargo.toml 10 | Since this library is currently unstable, you may want to simply grab the latest version from `master`. 11 | ```bash 12 | satellite = { git = "https://github.com/richinfante/satellite-rs.git", branch = "master" } 13 | ``` 14 | 15 | ### How to use 16 | 1. Get updated tles from a source such as [celestrak](https://celestrak.com/NORAD/elements/stations.txt). 17 | 2. Run predictions for a specific location / time (see below.) 18 | 19 | ### Example: 20 | ```rust 21 | extern crate satellite; 22 | extern crate chrono; 23 | use chrono::prelude::*; 24 | 25 | fn main() { 26 | // ISS TLE's 27 | let tle1 = "1 25544U 98067A 19070.20068744 .00000619 00000-0 17310-4 0 9990"; 28 | let tle2 = "2 25544 51.6414 128.3903 0004102 93.2843 5.7821 15.52799004160030"; 29 | 30 | let mut satrec = satellite::io::twoline2satrec(tle1, tle2).unwrap(); 31 | 32 | let time = Utc::now(); 33 | let result = satellite::propogation::propogate_datetime(&mut satrec, time).unwrap(); 34 | 35 | println!("Position {:#?}", result.position); 36 | println!("Velocity {:#?}", result.velocity); 37 | 38 | // Set the Observer at 122.03 West by 36.96 North, in RADIANS 39 | let observer = satellite::Geodedic { 40 | longitude: -122.0308 * satellite::constants::DEG_2_RAD, 41 | latitude: 36.9613422 * satellite::constants::DEG_2_RAD, 42 | height: 0.370 43 | }; 44 | 45 | let gmst = satellite::propogation::gstime::gstime_datetime(time); 46 | let sat_pos = satellite::transforms::eci_to_geodedic(&result.position, gmst); 47 | let position_ecf = satellite::transforms::eci_to_ecf(&result.position, gmst); 48 | let look_angles = satellite::transforms::ecf_to_look_angles(&observer, &position_ecf); 49 | 50 | println!("longitude = {}", sat_pos.latitude * satellite::constants::RAD_TO_DEG); 51 | println!("latitude = {}", sat_pos.longitude * satellite::constants::RAD_TO_DEG); 52 | println!("alt = {}", sat_pos.height * satellite::constants::KM_TO_MI); 53 | println!("aizmuth = {}", look_angles.azimuth * satellite::constants::RAD_TO_DEG); 54 | println!("elevation = {}", look_angles.elevation * satellite::constants::RAD_TO_DEG); 55 | println!("range = {}", look_angles.range * satellite::constants::KM_TO_MI); 56 | } 57 | ``` 58 | 59 | ### Known Issues: 60 | - Deep-space calculations appear to be slightly off. Cause is currently unknown 61 | - Many portions are currently untested. 62 | 63 | ### Todo List: 64 | - Refactor code to allow for easier testing 65 | - Add tests for remaining modules to ensure proper port 66 | - [x] constants.rs 67 | - [ ] ext.rs 68 | - [ ] doppler_factor.rs 69 | - [ ] transforms.rs _partial_ 70 | - [x] io.rs 71 | - [x] propogation/dpper.rs 72 | - [x] propogation/dscom.rs 73 | - [x] propogation/dsinit.rs 74 | - [x] propogation/dspace.rs 75 | - [x] propogation/gstime.rs 76 | - [x] propogation/initl.rs 77 | - [ ] propogation/sgp4.rs 78 | - [ ] propogation/sgp4init.rs 79 | 80 | MIT License. Derivative of [satellite-js](https://github.com/shashwatak/satellite-js) and [sgp4](https://pypi.org/project/sgp4/) 81 | -------------------------------------------------------------------------------- /src/ext.rs: -------------------------------------------------------------------------------- 1 | use chrono::prelude::*; 2 | use chrono::DateTime; 3 | 4 | pub struct MDHMS { 5 | pub month: f64, 6 | pub day: f64, 7 | pub hour: f64, 8 | pub minute: f64, 9 | pub second: f64, 10 | } 11 | 12 | /* ----------------------------------------------------------------------------- 13 | * 14 | * procedure days2mdhms 15 | * 16 | * this procedure converts the day of the year, days, to the equivalent month 17 | * day, hour, minute and second. 18 | * 19 | * algorithm : set up array for the number of days per month 20 | * find leap year - use 1900 because 2000 is a leap year 21 | * loop through a temp value while the value is < the days 22 | * perform int conversions to the correct day and month 23 | * convert remainder into h m s using type conversions 24 | * 25 | * author : david vallado 719-573-2600 1 mar 2001 26 | * 27 | * inputs description range / units 28 | * year - year 1900 .. 2100 29 | * days - julian day of the year 0.0 .. 366.0 30 | * 31 | * outputs : 32 | * mon - month 1 .. 12 33 | * day - day 1 .. 28,29,30,31 34 | * hr - hour 0 .. 23 35 | * min - minute 0 .. 59 36 | * sec - second 0.0 .. 59.999 37 | * 38 | * locals : 39 | * dayofyr - day of year 40 | * temp - temporary extended values 41 | * inttemp - temporary int value 42 | * i - index 43 | * lmonth[12] - int array containing the number of days per month 44 | * 45 | * coupling : 46 | * none. 47 | * --------------------------------------------------------------------------- */ 48 | pub fn days2mdhms(year: u64, days: f64) -> MDHMS { 49 | let mut lmonth: [u64; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 50 | if year % 4 == 0 { 51 | lmonth[1] = 29 52 | } 53 | 54 | let dayofyr = days.floor() as u64; 55 | 56 | let mut i: u64 = 1; 57 | let mut inttemp: u64 = 0; 58 | 59 | while dayofyr > (inttemp + lmonth[(i - 1) as usize]) && i < 12 { 60 | inttemp += lmonth[(i - 1) as usize]; 61 | i += 1; 62 | } 63 | 64 | let month = i as f64; 65 | let day = (dayofyr - inttemp) as f64; 66 | let mut temp = (days - dayofyr as f64) * 24.0; 67 | let hour = temp.floor(); 68 | temp = (temp - hour as f64) * 60.0; 69 | let minute = temp.floor(); 70 | let second = (temp - minute as f64) * 60.0; 71 | 72 | MDHMS { 73 | month, 74 | hour, 75 | day, 76 | minute, 77 | second, 78 | } 79 | } 80 | 81 | pub fn jday(year: f64, mon: f64, day: f64, hr: f64, minute: f64, sec: f64, msec: f64) -> f64 { 82 | ((367.0 * year) - ((7.0 * (year + ((mon + 9.0) / 12.0).floor())) * 0.25).floor()) 83 | + ((275.0 * mon) / 9.0).floor() 84 | + day 85 | + 1721013.5 86 | + (((((msec / 60000.0) + (sec / 60.0) + minute) / 60.0) + hr) / 24.0) // ut in days 87 | } 88 | 89 | pub fn jday_datetime(time: DateTime) -> f64 { 90 | jday( 91 | time.year() as f64, 92 | time.month() as f64, 93 | time.day() as f64, 94 | time.hour() as f64, 95 | time.minute() as f64, 96 | time.second() as f64, 97 | 0 as f64 98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /src/transforms.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | use crate::*; 3 | use crate::Bearing; 4 | use crate::Geodedic; 5 | use crate::TopoCentric; 6 | use crate::Vec3; 7 | 8 | pub fn radians_to_degrees(radians: f64) -> f64 { 9 | return radians * RAD_TO_DEG; 10 | } 11 | 12 | pub fn degrees_to_radians(degrees: f64) -> f64 { 13 | return degrees * DEG_2_RAD; 14 | } 15 | 16 | pub fn degrees_lat(radians: f64) -> f64 { 17 | if radians < -std::f64::consts::FRAC_PI_2 || radians > std::f64::consts::FRAC_PI_2 { 18 | panic!("Range error: Latitude radians must be in range [-pi/2; pi/2].") 19 | } 20 | 21 | radians_to_degrees(radians) 22 | } 23 | 24 | pub fn degrees_long(radians: f64) -> f64 { 25 | if radians < -std::f64::consts::PI || radians > std::f64::consts::PI { 26 | panic!("Range error: Longitude radians must be in range [-pi; pi].") 27 | } 28 | 29 | radians_to_degrees(radians) 30 | } 31 | 32 | pub fn radians_lat(degrees: f64) -> f64 { 33 | if degrees < -90.0 || degrees > 90.0 { 34 | panic!("RangeError: Latitude degrees must be in range [-90; 90].") 35 | } 36 | 37 | degrees_to_radians(degrees) 38 | } 39 | 40 | pub fn radians_long(degrees: f64) -> f64 { 41 | if degrees < -180.0 || degrees > 180.0 { 42 | panic!("RangeError: Latitude degrees must be in range [-180; 180].") 43 | } 44 | 45 | degrees_to_radians(degrees) 46 | } 47 | 48 | pub fn geodedic_to_ecf(geodetic: &Geodedic) -> Ecf { 49 | const A: f64 = 6378.137; 50 | const B: f64 = 6356.7523142; 51 | const F: f64 = (A - B) / A; 52 | const E2: f64 = ((2.0 * F) - (F * F)); 53 | 54 | let normal = A / (1.0 - (E2 * geodetic.latitude.sin().powf(2.0))).sqrt(); 55 | 56 | let x = (normal + geodetic.height) * geodetic.latitude.cos() * geodetic.longitude.cos(); 57 | let y = (normal + geodetic.height) * geodetic.latitude.cos() * geodetic.longitude.sin(); 58 | let z = ((normal * (1.0 - E2)) + geodetic.height) * geodetic.latitude.sin(); 59 | 60 | Vec3 { x, y, z } 61 | } 62 | 63 | pub fn eci_to_geodedic(eci: &Eci, gmst: f64) -> Geodedic { 64 | const A: f64 = 6378.137; 65 | const B: f64 = 6356.7523142; 66 | let r: f64 = ((eci.x * eci.x) + (eci.y * eci.y)).sqrt(); 67 | const F: f64 = (A - B) / A; 68 | const E2: f64 = ((2.0 * F) - (F * F)); 69 | 70 | let mut longitude = eci.y.atan2(eci.x) - gmst; 71 | 72 | while longitude < -PI { 73 | longitude += TWO_PI; 74 | } 75 | 76 | while longitude > PI { 77 | longitude -= TWO_PI; 78 | } 79 | 80 | const KMAX: i32 = 20; 81 | 82 | let mut latitude = eci.z.atan2((eci.x.powi(2) + eci.y.powi(2)).sqrt()); 83 | 84 | let mut k: i32 = 0; 85 | let mut c: f64 = 0.0; 86 | 87 | while k < KMAX { 88 | c = 1.0 / (1.0 - (E2 * latitude.sin().powi(2))); 89 | latitude = (eci.z + (A * c * E2 * latitude.sin())).atan2(r); 90 | k += 1; 91 | } 92 | 93 | let height = (r / latitude.cos()) - (A * c); 94 | 95 | Geodedic { 96 | latitude, 97 | longitude, 98 | height, 99 | } 100 | } 101 | 102 | pub fn ecf_to_eci(ecf: &Ecf, gmst: f64) -> Eci { 103 | let x = (ecf.x * gmst.cos()) - (ecf.y * gmst.sin()); 104 | let y = (ecf.x * gmst.sin()) + (ecf.y * gmst.cos()); 105 | let z = ecf.z; 106 | 107 | Vec3 { x, y, z } 108 | } 109 | 110 | pub fn eci_to_ecf(eci: &Eci, gmst: f64) -> Ecf { 111 | let x = (eci.x * gmst.cos()) + (eci.y * gmst.sin()); 112 | let y = (eci.x * -gmst.sin()) + (eci.y * gmst.cos()); 113 | let z = eci.z; 114 | 115 | Vec3 { x, y, z } 116 | } 117 | 118 | pub fn topocentric(observer: &Geodedic, satellite: &Ecf) -> TopoCentric { 119 | let observer_ecf = geodedic_to_ecf(&observer); 120 | 121 | let r = satellite.subtract(&observer_ecf); 122 | 123 | let top_s = (observer.latitude.sin() * observer.longitude.cos() * r.x) 124 | + (observer.latitude.sin() * observer.longitude.sin() * r.y) 125 | - (observer.latitude.cos() * r.z); 126 | 127 | let top_e = (-observer.longitude.sin() * r.x) + (observer.longitude.cos() * r.y); 128 | 129 | let top_z = (observer.latitude.cos() * observer.longitude.cos() * r.x) 130 | + (observer.latitude.cos() * observer.longitude.sin() * r.y) 131 | + (observer.latitude.sin() * r.z); 132 | 133 | TopoCentric { 134 | top_s, 135 | top_e, 136 | top_z, 137 | } 138 | } 139 | 140 | pub fn topocentric_to_look_angles(tc: &TopoCentric) -> Bearing { 141 | let range_sat = (tc.top_s.powi(2) + tc.top_e.powi(2) + tc.top_z.powi(2)).sqrt(); 142 | let el = (tc.top_z / range_sat).asin(); 143 | let az = (-tc.top_e).atan2(tc.top_s) + std::f64::consts::PI; 144 | 145 | Bearing { 146 | range: range_sat, 147 | elevation: el, 148 | azimuth: az, 149 | } 150 | } 151 | 152 | pub fn ecf_to_look_angles(observer: &Geodedic, satellite: &Ecf) -> Bearing { 153 | let topocentric = topocentric(observer, satellite); 154 | 155 | topocentric_to_look_angles(&topocentric) 156 | } 157 | 158 | #[cfg(test)] 159 | mod test { 160 | use crate::tests::*; 161 | use crate::*; 162 | #[test] 163 | fn eci_to_ecf() { 164 | let position = Eci { 165 | x: 2328.957357263014, 166 | y: -5995.219305262678, 167 | z: 1720.0073114076358, 168 | }; 169 | 170 | let position_ecf = transforms::eci_to_ecf(&position, 0.0); 171 | 172 | assert_eq!( 173 | position_ecf, 174 | Ecf { 175 | x: 2328.957357263014, 176 | y: -5995.219305262678, 177 | z: 1720.0073114076358 178 | } 179 | ); 180 | } 181 | 182 | #[test] 183 | fn ecf_to_eci() { 184 | let position = Eci { 185 | x: 2328.957357263014, 186 | y: -5995.219305262678, 187 | z: 1720.0073114076358, 188 | }; 189 | 190 | let position_ecf = transforms::eci_to_ecf(&position, 0.0); 191 | let orig_eci = transforms::ecf_to_eci(&position_ecf, 0.0); 192 | assert_eq!(position, orig_eci); 193 | } 194 | 195 | #[test] 196 | fn ecf_to_look_angles() { 197 | let observer_gd = Geodedic { 198 | longitude: -122.0308 * constants::DEG_2_RAD, 199 | latitude: 36.9613422 * constants::DEG_2_RAD, 200 | height: 0.370, 201 | }; 202 | 203 | let position_ecf = Ecf { 204 | x: 2328.957357263014, 205 | y: -5995.219305262678, 206 | z: 1720.0073114076358, 207 | }; 208 | 209 | let res = transforms::ecf_to_look_angles(&observer_gd, &position_ecf); 210 | 211 | assert_diff(res.azimuth, 1.747132515004105, 1e-12); 212 | assert_diff(res.elevation, -0.40791001471599636, 1e-12); 213 | assert_diff(res.range, 5703.24291019934, 1e-11); 214 | } 215 | 216 | #[test] 217 | fn topocentric() { 218 | let observer_gd = Geodedic { 219 | longitude: -122.0308 * constants::DEG_2_RAD, 220 | latitude: 36.9613422 * constants::DEG_2_RAD, 221 | height: 0.370, 222 | }; 223 | 224 | let position_ecf = Ecf { 225 | x: 2328.957357263014, 226 | y: -5995.219305262678, 227 | z: 1720.0073114076358, 228 | }; 229 | 230 | let ts = transforms::topocentric(&observer_gd, &position_ecf); 231 | assert_diff(ts.top_s, 918.3964944158424, 1e-12); 232 | assert_diff(ts.top_e, 5154.118963234977, 1e-12); 233 | assert_diff(ts.top_z, -2262.429067309148, 1e-12); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate chrono; 2 | 3 | pub mod constants; 4 | pub mod doppler_factor; 5 | pub mod ext; 6 | pub mod io; 7 | pub mod propogation; 8 | pub mod transforms; 9 | 10 | #[derive(Debug, PartialEq)] 11 | /// Standard three-component vector (x,y,z) 12 | pub struct Vec3 { 13 | pub x: f64, 14 | pub y: f64, 15 | pub z: f64, 16 | } 17 | 18 | type Ecf = Vec3; 19 | type Eci = Vec3; 20 | 21 | #[derive(Debug, PartialEq)] 22 | pub struct TopoCentric { 23 | /// Positive horizontal vector S due south. 24 | pub top_s: f64, 25 | 26 | /// Positive horizontal vector E due east. 27 | pub top_e: f64, 28 | 29 | /// Vector Z normal to the surface of the earth (up). 30 | pub top_z: f64, 31 | } 32 | 33 | #[derive(Debug, PartialEq)] 34 | /// Latitude/Longitude/Height based position 35 | pub struct Geodedic { 36 | /// Longitude, in radians. 37 | pub longitude: f64, 38 | 39 | /// Longitude, in radians. 40 | pub latitude: f64, 41 | 42 | /// Altitude, in Km. 43 | pub height: f64, 44 | } 45 | 46 | #[derive(Debug, PartialEq)] 47 | /// Relative position vector 48 | pub struct Bearing { 49 | /// Aizmuth in radians 50 | pub azimuth: f64, 51 | 52 | /// Elevation in radians 53 | pub elevation: f64, 54 | 55 | /// Range in km 56 | pub range: f64, 57 | } 58 | 59 | impl Vec3 { 60 | pub fn range(&self, to: &Vec3) -> f64 { 61 | (((self.x - to.x).powf(2.0)) + ((self.y - to.y).powf(2.0)) + ((self.z - to.z).powf(2.0))) 62 | .sqrt() 63 | } 64 | 65 | pub fn add(&self, by: &Vec3) -> Vec3 { 66 | Vec3 { 67 | x: self.x + by.x, 68 | y: self.y + by.y, 69 | z: self.z + by.z, 70 | } 71 | } 72 | 73 | pub fn subtract(&self, by: &Vec3) -> Vec3 { 74 | Vec3 { 75 | x: self.x - by.x, 76 | y: self.y - by.y, 77 | z: self.z - by.z, 78 | } 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | pub fn assert_similar(lhs: f64, rhs: f64) { 85 | assert_diff(lhs, rhs, 1e-13); 86 | } 87 | 88 | pub fn assert_diff(lhs: f64, rhs: f64, epsilon: f64) { 89 | if (lhs - rhs).abs() > epsilon { 90 | panic!( 91 | "Assertion failed: diff between {} - {} > {}", 92 | lhs, rhs, epsilon 93 | ); 94 | } 95 | } 96 | 97 | use crate::Vec3; 98 | struct TrackEntry { 99 | time: f64, 100 | known_pos: Vec3, 101 | known_vel: Vec3, 102 | } 103 | 104 | #[test] 105 | fn leo_tle() { 106 | let tle1 = "1 88888U 80275.98708465 .00073094 13844-3 66816-4 0 8"; 107 | let tle2 = "2 88888 72.8435 115.9689 0086731 52.6988 110.5714 16.05824518 105"; 108 | 109 | let mut satrec = crate::io::twoline2satrec(tle1, tle2).unwrap(); 110 | 111 | let known_track: Vec = vec![ 112 | TrackEntry { 113 | time: 0.0, 114 | known_pos: Vec3 { 115 | x: 2328.97048951, 116 | y: -5995.22076416, 117 | z: 1719.97067261, 118 | }, 119 | known_vel: Vec3 { 120 | x: 2.91207230, 121 | y: -0.98341546, 122 | z: -7.09081703, 123 | }, 124 | }, 125 | TrackEntry { 126 | time: 360.0, 127 | known_pos: Vec3 { 128 | x: 2456.10705566, 129 | y: -6071.93853760, 130 | z: 1222.89727783, 131 | }, 132 | known_vel: Vec3 { 133 | x: 2.67938992, 134 | y: -0.44829041, 135 | z: -7.22879231, 136 | }, 137 | }, 138 | ]; 139 | 140 | for entry in known_track { 141 | let result = match crate::propogation::sgp4::sgp4(&mut satrec, entry.time) { 142 | Ok(res) => res, 143 | Err(err) => panic!("Propogation Error: {:#?} (code {})", err, err.code()) 144 | }; 145 | 146 | // println!("pos @{} {:#?}", entry.time, result.position); 147 | // println!("vel @{} {:#?}", entry.time, result.velocity); 148 | let diff_pos = result.position.subtract(&entry.known_pos); 149 | // println!("distance to pos: @{} {:#?}", entry.time, diff_pos); 150 | 151 | let diff_vel = result.velocity.subtract(&entry.known_vel); 152 | // println!("distance to vel: @{} {:#?}", entry.time, diff_vel); 153 | // println!( 154 | // "{} < {} = {}", 155 | // diff_pos.x.abs(), 156 | // 0.1, 157 | // diff_pos.x.abs() < 0.1 158 | // ); 159 | assert!(diff_pos.x.abs() < 0.1); 160 | assert!(diff_pos.y.abs() < 0.1); 161 | assert!(diff_pos.z.abs() < 0.1); 162 | 163 | assert!(diff_vel.x.abs() < 0.1); 164 | assert!(diff_vel.y.abs() < 0.1); 165 | assert!(diff_vel.z.abs() < 0.1); 166 | 167 | // assert_eq!(result.position.range(&entry.known_pos), 0.0); 168 | // assert_eq!(result.velocity.range(&entry.known_vel), 0.0); 169 | } 170 | } 171 | 172 | #[test] 173 | fn ds_tle() { 174 | let tle1 = "1 11801U 80230.29629788 .01431103 00000-0 14311-1 "; 175 | let tle2 = "2 11801 46.7916 230.4354 7318036 47.4722 10.4117 2.28537848 "; 176 | 177 | let mut satrec = crate::io::twoline2satrec(tle1, tle2).unwrap(); 178 | 179 | let known_track: Vec = vec![ 180 | TrackEntry { 181 | time: 0.0, 182 | known_pos: Vec3 { 183 | x: 7473.37066650, 184 | y: 428.95261765, 185 | z: 5828.74786377, 186 | }, 187 | known_vel: Vec3 { 188 | x: 5.10715413, 189 | y: 6.44468284, 190 | z: -0.18613096, 191 | }, 192 | }, 193 | TrackEntry { 194 | time: 360.0, 195 | known_pos: Vec3 { 196 | x: -3305.22537232, 197 | y: 32410.86328125, 198 | z: -24697.17675781, 199 | }, 200 | known_vel: Vec3 { 201 | x: -1.30113538, 202 | y: -1.15131518, 203 | z: -0.28333528, 204 | }, 205 | }, 206 | ]; 207 | 208 | for entry in known_track { 209 | let result = match crate::propogation::sgp4::sgp4(&mut satrec, entry.time) { 210 | Ok(res) => res, 211 | Err(err) => panic!("Propogation Error: {:#?} (code {})", err, err.code()) 212 | }; 213 | 214 | // println!("pos @{} {:#?}", entry.time, result.position); 215 | // println!("vel @{} {:#?}", entry.time, result.velocity); 216 | let diff_pos = result.position.subtract(&entry.known_pos); 217 | // println!("distance to pos: @{} {:#?}", entry.time, diff_pos); 218 | 219 | let diff_vel = result.velocity.subtract(&entry.known_vel); 220 | // println!("distance to vel: @{} {:#?}", entry.time, diff_vel); 221 | // println!( 222 | // "{} < {} = {}", 223 | // diff_pos.x.abs(), 224 | // 0.1, 225 | // diff_pos.x.abs() < 0.1 226 | // ); 227 | 228 | // TODO: these seem off. 229 | assert!(diff_pos.x.abs() < 50.0); 230 | assert!(diff_pos.y.abs() < 50.0); 231 | assert!(diff_pos.z.abs() < 50.0); 232 | 233 | assert!(diff_vel.x.abs() < 0.1); 234 | assert!(diff_vel.y.abs() < 0.1); 235 | assert!(diff_vel.z.abs() < 0.1); 236 | } 237 | } 238 | 239 | #[test] 240 | fn test_parse_invalid_tle() { 241 | use crate::io::SatrecParseError; 242 | use chrono::prelude::*; 243 | 244 | let (sats, errors) = crate::io::parse_multiple("Test"); 245 | assert_eq!(errors, vec![ 246 | SatrecParseError::SatrecMultiError(0, Box::new(SatrecParseError::InvalidTLEBadLineCount)) 247 | ]); 248 | } 249 | } 250 | 251 | -------------------------------------------------------------------------------- /src/propogation/initl.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | use crate::propogation::dpper::DpperOpsMode; 3 | use crate::propogation::gstime; 4 | /*----------------------------------------------------------------------------- 5 | * 6 | * procedure initl 7 | * 8 | * this procedure initializes the sgp4 propagator. all the initialization is 9 | * consolidated here instead of having multiple loops inside other routines. 10 | * 11 | * author : david vallado 719-573-2600 28 jun 2005 12 | * 13 | * inputs : 14 | * ecco - eccentricity 0.0 - 1.0 15 | * epoch - epoch time in days from jan 0, 1950. 0 hr 16 | * inclo - inclination of satellite 17 | * no - mean motion of satellite 18 | * satn - satellite number 19 | * 20 | * outputs : 21 | * ainv - 1.0 / a 22 | * ao - semi major axis 23 | * con41 - 24 | * con42 - 1.0 - 5.0 cos(i) 25 | * cosio - cosine of inclination 26 | * cosio2 - cosio squared 27 | * eccsq - eccentricity squared 28 | * method - flag for deep space 'd', 'n' 29 | * omeosq - 1.0 - ecco * ecco 30 | * posq - semi-parameter squared 31 | * rp - radius of perigee 32 | * rteosq - square root of (1.0 - ecco*ecco) 33 | * sinio - sine of inclination 34 | * gsto - gst at time of observation rad 35 | * no - mean motion of satellite 36 | * 37 | * locals : 38 | * ak - 39 | * d1 - 40 | * del - 41 | * adel - 42 | * po - 43 | * 44 | * coupling : 45 | * getgravconst 46 | * gstime - find greenwich sidereal time from the julian date 47 | * 48 | * references : 49 | * hoots, roehrich, norad spacetrack report #3 1980 50 | * hoots, norad spacetrack report #6 1986 51 | * hoots, schumacher and glover 2004 52 | * vallado, crawford, hujsak, kelso 2006 53 | ----------------------------------------------------------------------------*/ 54 | 55 | pub struct InitlOptions { 56 | pub opsmode: DpperOpsMode, 57 | pub method: InitlMethod, 58 | 59 | pub satn: String, 60 | pub ecco: f64, 61 | pub epoch: f64, 62 | pub inclo: f64, 63 | pub no: f64, 64 | } 65 | 66 | #[derive(PartialEq, Debug)] 67 | pub struct InitlReturn { 68 | pub no: f64, 69 | pub method: InitlMethod, 70 | pub ainv: f64, 71 | pub ao: f64, 72 | pub con41: f64, 73 | pub con42: f64, 74 | pub cosio: f64, 75 | pub cosio2: f64, 76 | pub eccsq: f64, 77 | pub omeosq: f64, 78 | pub posq: f64, 79 | pub rp: f64, 80 | pub rteosq: f64, 81 | pub sinio: f64, 82 | pub gsto: f64, 83 | } 84 | 85 | #[derive(PartialEq, Clone, Debug)] 86 | pub enum InitlMethod { 87 | N, 88 | D, 89 | } 90 | 91 | pub fn initl(options: InitlOptions) -> InitlReturn { 92 | let InitlOptions { 93 | ecco, 94 | epoch, 95 | inclo, 96 | opsmode, 97 | .. 98 | } = options; 99 | 100 | let InitlOptions { mut no, .. } = options; 101 | 102 | // sgp4fix use old way of finding gst 103 | // ----------------------- earth constants --------------------- 104 | // sgp4fix identify constants and allow alternate values 105 | 106 | // ------------- calculate auxillary epoch quantities ---------- 107 | let eccsq = ecco * ecco; 108 | let omeosq = 1.0 - eccsq; 109 | let rteosq = omeosq.sqrt(); 110 | let cosio = inclo.cos(); 111 | let cosio2 = cosio * cosio; 112 | 113 | // ------------------ un-kozai the mean motion ----------------- 114 | let ak = (XKE / no).powf(X2O3); 115 | let d1 = (0.75 * J2 * ((3.0 * cosio2) - 1.0)) / (rteosq * omeosq); 116 | let mut del_prime = d1 / (ak * ak); 117 | let adel = ak 118 | * (1.0 119 | - (del_prime * del_prime) 120 | - (del_prime * ((1.0 / 3.0) + ((134.0 * del_prime * del_prime) / 81.0)))); 121 | del_prime = d1 / (adel * adel); 122 | no /= 1.0 + del_prime; 123 | 124 | let ao = (XKE / no).powf(X2O3); 125 | let sinio = inclo.sin(); 126 | let po = ao * omeosq; 127 | let con42 = 1.0 - (5.0 * cosio2); 128 | let con41 = -con42 - cosio2 - cosio2; 129 | let ainv = 1.0 / ao; 130 | let posq = po * po; 131 | let rp = ao * (1.0 - ecco); 132 | let method = InitlMethod::N; 133 | 134 | // sgp4fix modern approach to finding sidereal time 135 | let mut gsto; 136 | if opsmode == DpperOpsMode::A { 137 | // sgp4fix use old way of finding gst 138 | // count integer number of days from 0 jan 1970 139 | let ts70 = epoch - 7305.0; 140 | let ds70 = (ts70 + 1.0e-8).floor(); 141 | let tfrac = ts70 - ds70; 142 | 143 | // find greenwich location at epoch 144 | let c1 = 1.72027916940703639e-2; 145 | let thgr70 = 1.7321343856509374; 146 | let fk5r = 5.07551419432269442e-15; 147 | let c1p2p = c1 + TWO_PI; 148 | gsto = (thgr70 + (c1 * ds70) + (c1p2p * tfrac) + (ts70 * ts70 * fk5r)) % TWO_PI; 149 | if gsto < 0.0 { 150 | gsto += TWO_PI; 151 | } 152 | } else { 153 | gsto = gstime::gstime(epoch + 2433281.5); 154 | } 155 | 156 | InitlReturn { 157 | no, 158 | 159 | method, 160 | 161 | ainv, 162 | ao, 163 | con41, 164 | con42, 165 | cosio, 166 | 167 | cosio2, 168 | eccsq, 169 | omeosq, 170 | posq, 171 | 172 | rp, 173 | rteosq, 174 | sinio, 175 | gsto, 176 | } 177 | } 178 | 179 | #[cfg(test)] 180 | mod test { 181 | use crate::propogation::dpper::*; 182 | use crate::propogation::initl::*; 183 | use crate::tests::assert_similar; 184 | 185 | #[test] 186 | fn test_initl() { 187 | let opts = InitlOptions { 188 | satn: "88888".to_string(), 189 | ecco: 0.0086731, 190 | epoch: 11232.987084649969, 191 | inclo: 1.2713589136764896, 192 | no: 0.07006731262087737, 193 | method: InitlMethod::N, 194 | opsmode: DpperOpsMode::I, 195 | }; 196 | 197 | let result = initl(opts); 198 | 199 | assert_eq!(result.no, 0.07010615621239219); 200 | assert_eq!(result.method, InitlMethod::N); 201 | assert_eq!(result.ainv, 0.9614303648645832); 202 | assert_eq!(result.ao, 1.0401169305078577); 203 | assert_eq!(result.con41, -0.7389556198424165); 204 | assert_eq!(result.con42, 0.5649260330706942); 205 | assert_eq!(result.cosio, 0.2949827001467394); 206 | assert_eq!(result.cosio2, 0.08701479338586116); 207 | assert_eq!(result.eccsq, 0.00007522266360999999); 208 | assert_eq!(result.omeosq, 0.99992477733639); 209 | assert_eq!(result.posq, 1.0816804769920354); 210 | assert_eq!(result.rp, 1.03109589235787); 211 | assert_eq!(result.rteosq, 0.9999623879608622); 212 | assert_eq!(result.sinio, 0.9555025937244435); 213 | assert_eq!(result.gsto, 0.1082901416688955); 214 | } 215 | 216 | #[test] 217 | fn test_initl_ds() { 218 | let opts = InitlOptions { 219 | satn: "11801".to_string(), 220 | ecco: 0.7318036, 221 | epoch: 11187.29629787989, 222 | inclo: 0.8166674822761788, 223 | no: 0.009971844782555844, 224 | method: InitlMethod::N, 225 | opsmode: DpperOpsMode::I, 226 | }; 227 | 228 | let res = initl(opts); 229 | assert_eq!(res.no, 0.009971131594572634); 230 | assert_eq!(res.method, InitlMethod::N); 231 | assert_eq!(res.ainv, 0.2619650549686258); 232 | assert_eq!(res.ao, 3.8173030373068833); 233 | assert_similar(res.con41, 0.40625317982989756); 234 | assert_similar(res.con42, -1.3437552997164959); 235 | assert_similar(res.cosio, 0.6846539709541596); 236 | assert_similar(res.cosio2, 0.4687510599432992); 237 | assert_similar(res.eccsq, 0.53553650897296); 238 | assert_eq!(res.omeosq, 0.46446349102704); 239 | assert_eq!(res.posq, 3.143521535730025); 240 | assert_eq!(res.rp, 1.0237869323147717); 241 | assert_eq!(res.rteosq, 0.6815155838475302); 242 | assert_similar(res.sinio, 0.7288682597401953); 243 | assert_eq!(res.gsto, 1.265125075734467); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/propogation/dspace.rs: -------------------------------------------------------------------------------- 1 | use std::f64::consts::PI; 2 | const TWO_PI: f64 = PI * 2.0; 3 | 4 | pub struct DspaceOptions { 5 | pub irez: f64, 6 | pub d2201: f64, 7 | pub d2211: f64, 8 | pub d3210: f64, 9 | pub d3222: f64, 10 | pub d4410: f64, 11 | pub d4422: f64, 12 | pub d5220: f64, 13 | pub d5232: f64, 14 | pub d5421: f64, 15 | pub d5433: f64, 16 | pub dedt: f64, 17 | pub del1: f64, 18 | pub del2: f64, 19 | pub del3: f64, 20 | pub didt: f64, 21 | pub dmdt: f64, 22 | pub dnodt: f64, 23 | pub domdt: f64, 24 | pub argpo: f64, 25 | pub argpdot: f64, 26 | pub t: f64, 27 | pub tc: f64, 28 | pub gsto: f64, 29 | pub xfact: f64, 30 | pub xlamo: f64, 31 | pub no: f64, 32 | pub atime: f64, 33 | pub em: f64, 34 | pub argpm: f64, 35 | pub inclm: f64, 36 | pub xli: f64, 37 | pub mm: f64, 38 | pub xni: f64, 39 | pub nodem: f64, 40 | pub nm: f64, 41 | } 42 | 43 | pub struct DspaceResult { 44 | pub atime: f64, 45 | pub em: f64, 46 | pub argpm: f64, 47 | pub inclm: f64, 48 | pub xli: f64, 49 | pub mm: f64, 50 | pub xni: f64, 51 | pub nodem: f64, 52 | pub dndt: f64, 53 | pub nm: f64, 54 | } 55 | /*----------------------------------------------------------------------------- 56 | * 57 | * procedure dspace 58 | * 59 | * this procedure provides deep space contributions to mean elements for 60 | * perturbing third body. these effects have been averaged over one 61 | * revolution of the sun and moon. for earth resonance effects, the 62 | * effects have been averaged over no revolutions of the satellite. 63 | * (mean motion) 64 | * 65 | * author : david vallado 719-573-2600 28 jun 2005 66 | * 67 | * inputs : 68 | * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 - 69 | * dedt - 70 | * del1, del2, del3 - 71 | * didt - 72 | * dmdt - 73 | * dnodt - 74 | * domdt - 75 | * irez - flag for resonance 0-none, 1-one day, 2-half day 76 | * argpo - argument of perigee 77 | * argpdot - argument of perigee dot (rate) 78 | * t - time 79 | * tc - 80 | * gsto - gst 81 | * xfact - 82 | * xlamo - 83 | * no - mean motion 84 | * atime - 85 | * em - eccentricity 86 | * ft - 87 | * argpm - argument of perigee 88 | * inclm - inclination 89 | * xli - 90 | * mm - mean anomaly 91 | * xni - mean motion 92 | * nodem - right ascension of ascending node 93 | * 94 | * outputs : 95 | * atime - 96 | * em - eccentricity 97 | * argpm - argument of perigee 98 | * inclm - inclination 99 | * xli - 100 | * mm - mean anomaly 101 | * xni - 102 | * nodem - right ascension of ascending node 103 | * dndt - 104 | * nm - mean motion 105 | * 106 | * locals : 107 | * delt - 108 | * ft - 109 | * theta - 110 | * x2li - 111 | * x2omi - 112 | * xl - 113 | * xldot - 114 | * xnddt - 115 | * xndt - 116 | * xomi - 117 | * 118 | * coupling : 119 | * none - 120 | * 121 | * references : 122 | * hoots, roehrich, norad spacetrack report #3 1980 123 | * hoots, norad spacetrack report #6 1986 124 | * hoots, schumacher and glover 2004 125 | * vallado, crawford, hujsak, kelso 2006 126 | ----------------------------------------------------------------------------*/ 127 | pub fn dspace(options: DspaceOptions) -> DspaceResult { 128 | let DspaceOptions { 129 | irez, 130 | d2201, 131 | d2211, 132 | d3210, 133 | d3222, 134 | d4410, 135 | d4422, 136 | d5220, 137 | d5232, 138 | d5421, 139 | d5433, 140 | dedt, 141 | del1, 142 | del2, 143 | del3, 144 | didt, 145 | dmdt, 146 | dnodt, 147 | domdt, 148 | argpo, 149 | argpdot, 150 | t, 151 | tc, 152 | gsto, 153 | xfact, 154 | xlamo, 155 | no, 156 | .. 157 | } = options; 158 | 159 | let DspaceOptions { 160 | mut atime, 161 | mut em, 162 | mut argpm, 163 | mut inclm, 164 | mut xli, 165 | mut mm, 166 | mut xni, 167 | mut nodem, 168 | mut nm, 169 | .. 170 | } = options; 171 | 172 | const FASX2: f64 = 0.13130908; 173 | const FASX4: f64 = 2.8843198; 174 | const FASX6: f64 = 0.37448087; 175 | const G22: f64 = 5.7686396; 176 | const G32: f64 = 0.95240898; 177 | const G44: f64 = 1.8014998; 178 | const G52: f64 = 1.0508330; 179 | const G54: f64 = 4.4108898; 180 | const RPTIM: f64 = 4.37526908801129966e-3; // equates to 7.29211514668855e-5 rad/sec 181 | const STEPP: f64 = 720.0; 182 | const STEPN: f64 = -720.0; 183 | const STEP2: f64 = 259200.0; 184 | 185 | let delt; 186 | let mut x2li; 187 | let mut x2omi; 188 | let xl; 189 | let mut xldot = 0.0; 190 | let mut xnddt = 0.0; 191 | let mut xndt = 0.0; 192 | let mut xomi; 193 | let mut dndt = 0.0; 194 | let mut ft = 0.0; 195 | 196 | // ----------- calculate deep space resonance effects ----------- 197 | let theta = (gsto + (tc * RPTIM)) % TWO_PI; 198 | em += dedt * t; 199 | 200 | inclm += didt * t; 201 | argpm += domdt * t; 202 | nodem += dnodt * t; 203 | mm += dmdt * t; 204 | 205 | // sgp4fix for negative inclinations 206 | // the following if statement should be commented out 207 | // if (inclm < 0.0) 208 | // { 209 | // inclm = -inclm; 210 | // argpm = argpm - pi; 211 | // nodem = nodem + pi; 212 | // } 213 | 214 | /* - update resonances : numerical (euler-maclaurin) integration - */ 215 | /* ------------------------- epoch restart ---------------------- */ 216 | // sgp4fix for propagator problems 217 | // the following integration works for negative time steps and periods 218 | // the specific changes are unknown because the original code was so convoluted 219 | 220 | // sgp4fix take out atime = 0.0 and fix for faster operation 221 | 222 | if irez != 0.0 { 223 | // sgp4fix streamline check 224 | if atime == 0.0 || t * atime <= 0.0 || t.abs() < atime.abs() { 225 | atime = 0.0; 226 | xni = no; 227 | xli = xlamo; 228 | } 229 | 230 | // sgp4fix move check outside loop 231 | if t > 0.0 { 232 | delt = STEPP; 233 | } else { 234 | delt = STEPN; 235 | } 236 | 237 | let mut iretn = 381; // added for do loop 238 | while iretn == 381 { 239 | // ------------------- dot terms calculated ------------- 240 | // ----------- near - synchronous resonance terms ------- 241 | if irez != 2.0 { 242 | xndt = (del1 * (xli - FASX2).sin()) 243 | + (del2 * (2.0 * (xli - FASX4)).sin()) 244 | + (del3 * (3.0 * (xli - FASX6)).sin()); 245 | xldot = xni + xfact; 246 | xnddt = (del1 * (xli - FASX2).cos()) 247 | + (2.0 * del2 * (2.0 * (xli - FASX4)).cos()) 248 | + (3.0 * del3 * (3.0 * (xli - FASX6)).cos()); 249 | xnddt *= xldot; 250 | } else { 251 | // --------- near - half-day resonance terms -------- 252 | xomi = argpo + (argpdot * atime); 253 | x2omi = xomi + xomi; 254 | x2li = xli + xli; 255 | xndt = (d2201 * ((x2omi + xli) - G22).sin()) 256 | + (d2211 * (xli - G22).sin()) 257 | + (d3210 * ((xomi + xli) - G32).sin()) 258 | + (d3222 * ((-xomi + xli) - G32).sin()) 259 | + (d4410 * ((x2omi + x2li) - G44).sin()) 260 | + (d4422 * (x2li - G44).sin()) 261 | + (d5220 * ((xomi + xli) - G52).sin()) 262 | + (d5232 * ((-xomi + xli) - G52).sin()) 263 | + (d5421 * ((xomi + x2li) - G54).sin()) 264 | + (d5433 * ((-xomi + x2li) - G54).sin()); 265 | xldot = xni + xfact; 266 | xnddt = (d2201 * ((x2omi + xli) - G22).cos()) 267 | + (d2211 * (xli - G22).cos()) 268 | + (d3210 * ((xomi + xli) - G32).cos()) 269 | + (d3222 * ((-xomi + xli) - G32).cos()) 270 | + (d5220 * ((xomi + xli) - G52).cos()) 271 | + (d5232 * ((-xomi + xli) - G52).cos()) 272 | + (2.0 * d4410 * ((x2omi + x2li) - G44).cos()) 273 | + (d4422 * (x2li - G44).cos()) 274 | + (d5421 * ((xomi + x2li) - G54).cos()) 275 | + (d5433 * ((-xomi + x2li) - G54).cos()); 276 | xnddt *= xldot; 277 | } 278 | 279 | // ----------------------- integrator ------------------- 280 | // sgp4fix move end checks to end of routine 281 | if (t - atime).abs() >= STEPP { 282 | iretn = 381; 283 | } else { 284 | ft = t - atime; 285 | iretn = 0; 286 | } 287 | 288 | if iretn == 381 { 289 | xli += (xldot * delt) + (xndt * STEP2); 290 | xni += (xndt * delt) + (xnddt * STEP2); 291 | atime += delt; 292 | } 293 | } 294 | 295 | nm = xni + (xndt * ft) + (xnddt * ft * ft * 0.5); 296 | xl = xli + (xldot * ft) + (xndt * ft * ft * 0.5); 297 | if irez != 1.0 { 298 | mm = (xl - (2.0 * nodem)) + (2.0 * theta); 299 | dndt = nm - no; 300 | } else { 301 | mm = (xl - nodem - argpm) + theta; 302 | dndt = nm - no; 303 | } 304 | nm = no + dndt; 305 | } 306 | 307 | DspaceResult { 308 | atime, 309 | em, 310 | argpm, 311 | inclm, 312 | xli, 313 | mm, 314 | xni, 315 | nodem, 316 | dndt, 317 | nm, 318 | } 319 | } 320 | 321 | #[cfg(test)] 322 | mod tests { 323 | use crate::propogation::dspace::*; 324 | 325 | #[test] 326 | fn test_dspace() { 327 | let opts = DspaceOptions { 328 | irez: 0.0, 329 | d2201: 0.0, 330 | d2211: 0.0, 331 | d3210: 0.0, 332 | d3222: 0.0, 333 | d4410: 0.0, 334 | d4422: 0.0, 335 | d5220: 0.0, 336 | d5232: 0.0, 337 | d5421: 0.0, 338 | d5433: 0.0, 339 | dedt: 2.63860646954029e-8, 340 | del1: 0.0, 341 | del2: 0.0, 342 | del3: 0.0, 343 | didt: -3.4767374233712414e-8, 344 | dmdt: 8.037814266648781e-8, 345 | dnodt: -6.033631312091549e-8, 346 | domdt: 9.465204025716937e-9, 347 | argpo: 0.8285461931652521, 348 | argpdot: 0.0000034607723715772176, 349 | t: 0.0, 350 | tc: 0.0, 351 | gsto: 1.265125075734467, 352 | xfact: 0.0, 353 | xlamo: 0.0, 354 | no: 0.009971131594572634, 355 | atime: 0.0, 356 | em: 0.7318036, 357 | argpm: 0.8285461931652521, 358 | inclm: 0.8166674822761788, 359 | xli: 0.0, 360 | mm: 0.1817184457298936, 361 | xni: 0.0, 362 | nodem: 4.021856443150141, 363 | nm: 0.009971131594572634, 364 | }; 365 | 366 | let res = dspace(opts); 367 | 368 | assert_eq!(res.atime, 0.0); 369 | assert_eq!(res.em, 0.7318036); 370 | assert_eq!(res.argpm, 0.8285461931652521); 371 | assert_eq!(res.inclm, 0.8166674822761788); 372 | assert_eq!(res.xli, 0.0); 373 | assert_eq!(res.mm, 0.1817184457298936); 374 | assert_eq!(res.xni, 0.0); 375 | assert_eq!(res.nodem, 4.021856443150141); 376 | assert_eq!(res.dndt, 0.0); 377 | assert_eq!(res.nm, 0.009971131594572634); 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/propogation/sgp4.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | use crate::io::Satrec; 3 | use crate::propogation::dpper::*; 4 | use crate::propogation::initl::InitlMethod; 5 | use crate::{Vec3, Eci}; 6 | /*---------------------------------------------------------------------------- 7 | * 8 | * procedure sgp4 9 | * 10 | * this procedure is the sgp4 prediction model from space command. this is an 11 | * updated and combined version of sgp4 and sdp4, which were originally 12 | * published separately in spacetrack report //3. this version follows the 13 | * methodology from the aiaa paper (2006) describing the history and 14 | * development of the code. 15 | * 16 | * author : david vallado 719-573-2600 28 jun 2005 17 | * 18 | * inputs : 19 | * satrec - initialised structure from sgp4init() call. 20 | * tsince - time since epoch (minutes) 21 | * 22 | * outputs : 23 | * r - position vector km 24 | * v - velocity km/sec 25 | * return code - non-zero on error. 26 | * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er 27 | * 2 - mean motion less than 0.0 28 | * 3 - pert elements, ecc < 0.0 or ecc > 1.0 29 | * 4 - semi-latus rectum < 0.0 30 | * 5 - epoch elements are sub-orbital 31 | * 6 - satellite has decayed 32 | * 33 | * locals : 34 | * am - 35 | * axnl, aynl - 36 | * betal - 37 | * cosim , sinim , cosomm , sinomm , cnod , snod , cos2u , 38 | * sin2u , coseo1 , sineo1 , cosi , sini , cosip , sinip , 39 | * cosisq , cossu , sinsu , cosu , sinu 40 | * delm - 41 | * delomg - 42 | * dndt - 43 | * eccm - 44 | * emsq - 45 | * ecose - 46 | * el2 - 47 | * eo1 - 48 | * eccp - 49 | * esine - 50 | * argpm - 51 | * argpp - 52 | * omgadf - 53 | * pl - 54 | * r - 55 | * rtemsq - 56 | * rdotl - 57 | * rl - 58 | * rvdot - 59 | * rvdotl - 60 | * su - 61 | * t2 , t3 , t4 , tc 62 | * tem5, temp , temp1 , temp2 , tempa , tempe , templ 63 | * u , ux , uy , uz , vx , vy , vz 64 | * inclm - inclination 65 | * mm - mean anomaly 66 | * nm - mean motion 67 | * nodem - right asc of ascending node 68 | * xinc - 69 | * xincp - 70 | * xl - 71 | * xlm - 72 | * mp - 73 | * xmdf - 74 | * xmx - 75 | * xmy - 76 | * nodedf - 77 | * xnode - 78 | * nodep - 79 | * np - 80 | * 81 | * coupling : 82 | * getgravconst- 83 | * dpper 84 | * dspace 85 | * 86 | * references : 87 | * hoots, roehrich, norad spacetrack report //3 1980 88 | * hoots, norad spacetrack report //6 1986 89 | * hoots, schumacher and glover 2004 90 | * vallado, crawford, hujsak, kelso 2006 91 | ----------------------------------------------------------------------------*/ 92 | 93 | #[derive(Debug, PartialEq)] 94 | pub struct SGP4Result { 95 | pub position: Eci, 96 | pub velocity: Vec3, 97 | } 98 | 99 | #[derive(Debug, PartialEq)] 100 | pub enum SGP4Error { 101 | Eccentricty, 102 | Ep, 103 | Nm, 104 | Pl, 105 | DecayCondition 106 | } 107 | 108 | impl SGP4Error { 109 | pub fn code(&self) -> u8 { 110 | match self { 111 | SGP4Error::Eccentricty => 1, 112 | SGP4Error::Nm => 2, 113 | SGP4Error::Ep => 3, 114 | SGP4Error::Pl => 4, 115 | SGP4Error::DecayCondition => 6 116 | } 117 | } 118 | } 119 | 120 | pub fn sgp4(satrec: &mut Satrec, tsince: f64) -> Result { 121 | let mut coseo1 = 0.0; 122 | let mut sineo1 = 0.0; 123 | let mut cosip; 124 | let mut sinip; 125 | let cosisq; 126 | let delm; 127 | let delomg; 128 | let mut eo1; 129 | let mut argpm; 130 | let argpp; 131 | let mut su; 132 | let t3; 133 | let t4; 134 | let tc; 135 | let mut tem5: f64; 136 | let mut temp; 137 | let mut tempa; 138 | let mut tempe; 139 | let mut templ; 140 | let inclm; 141 | let mut mm; 142 | let mut nm; 143 | let mut nodem; 144 | let mut xincp; 145 | let mut xlm; 146 | let mp; 147 | let nodep; 148 | 149 | /* ------------------ set mathematical constants --------------- */ 150 | // sgp4fix divisor for divide by zero check on inclination 151 | // the old check used 1.0 + cos(pi-1.0e-9), but then compared it to 152 | // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency 153 | 154 | const TEMP4: f64 = 1.5e-12; 155 | 156 | let vkmpersec: f64 = (EARTH_RADIUS * XKE) / 60.0; 157 | 158 | // --------------------- clear sgp4 error flag ----------------- 159 | satrec.t = tsince; 160 | satrec.error = 0; 161 | 162 | // ------- update for secular gravity and atmospheric drag ----- 163 | let xmdf = satrec.mo + (satrec.mdot * satrec.t); 164 | let argpdf = satrec.argpo + (satrec.argpdot * satrec.t); 165 | let nodedf = satrec.nodeo + (satrec.nodedot * satrec.t); 166 | argpm = argpdf; 167 | mm = xmdf; 168 | let t2 = satrec.t * satrec.t; 169 | nodem = nodedf + (satrec.nodecf * t2); 170 | tempa = 1.0 - (satrec.cc1 * satrec.t); 171 | tempe = satrec.bstar * satrec.cc4 * satrec.t; 172 | templ = satrec.t2cof * t2; 173 | 174 | if satrec.isimp != 1 { 175 | delomg = satrec.omgcof * satrec.t; 176 | // sgp4fix use mutliply for speed instead of pow 177 | let delmtemp = 1.0 + (satrec.eta * xmdf.cos()); 178 | delm = satrec.xmcof * ((delmtemp * delmtemp * delmtemp) - satrec.delmo); 179 | temp = delomg + delm; 180 | mm = xmdf + temp; 181 | argpm = argpdf - temp; 182 | t3 = t2 * satrec.t; 183 | t4 = t3 * satrec.t; 184 | tempa = tempa - (satrec.d2 * t2) - (satrec.d3 * t3) - (satrec.d4 * t4); 185 | tempe += satrec.bstar * satrec.cc5 * (mm.sin() - satrec.sinmao); 186 | templ = templ + (satrec.t3cof * t3) + (t4 * (satrec.t4cof + (satrec.t * satrec.t5cof))); 187 | } 188 | nm = satrec.no; 189 | let mut em = satrec.ecco; 190 | inclm = satrec.inclo; 191 | if satrec.method == InitlMethod::D { 192 | tc = satrec.t; 193 | 194 | let dspace_options = crate::propogation::dspace::DspaceOptions { 195 | irez: satrec.irez, 196 | d2201: satrec.d2201, 197 | d2211: satrec.d2211, 198 | d3210: satrec.d3210, 199 | d3222: satrec.d3222, 200 | d4410: satrec.d4410, 201 | d4422: satrec.d4422, 202 | d5220: satrec.d5220, 203 | d5232: satrec.d5232, 204 | d5421: satrec.d5421, 205 | d5433: satrec.d5433, 206 | dedt: satrec.dedt, 207 | del1: satrec.del1, 208 | del2: satrec.del2, 209 | del3: satrec.del3, 210 | didt: satrec.didt, 211 | dmdt: satrec.dmdt, 212 | dnodt: satrec.dnodt, 213 | domdt: satrec.domdt, 214 | argpo: satrec.argpo, 215 | argpdot: satrec.argpdot, 216 | t: satrec.t, 217 | tc, 218 | gsto: satrec.gsto, 219 | xfact: satrec.xfact, 220 | xlamo: satrec.xlamo, 221 | no: satrec.no, 222 | atime: satrec.atime, 223 | em, 224 | argpm, 225 | inclm, 226 | xli: satrec.xli, 227 | mm, 228 | xni: satrec.xni, 229 | nodem, 230 | nm, 231 | }; 232 | 233 | let dspace_result = crate::propogation::dspace::dspace(dspace_options); 234 | 235 | let crate::propogation::dspace::DspaceResult { 236 | argpm: _, 237 | inclm: _, 238 | mm: _, 239 | nodem: _, 240 | nm: _, 241 | .. 242 | } = dspace_result; 243 | em = dspace_result.em; 244 | } 245 | 246 | if nm <= 0.0 { 247 | // printf("// error nm %f\n", nm); 248 | satrec.error = 2; 249 | // sgp4fix add return 250 | // return [false, false]; 251 | // panic!("error 2") 252 | return Err(SGP4Error::Nm); 253 | } 254 | 255 | let am = ((XKE / nm).powf(X2O3)) * tempa * tempa; 256 | nm = XKE / (am.powf(1.5)); 257 | em -= tempe; 258 | 259 | // fix tolerance for error recognition 260 | // sgp4fix am is fixed from the previous nm check 261 | if em >= 1.0 || em < -0.001 { 262 | // || (am < 0.95) 263 | // printf("// error em %f\n", em); 264 | satrec.error = 1; 265 | // sgp4fix to return if there is an error in eccentricity 266 | // return [false, false]; 267 | // panic!("error 1"); 268 | return Err(SGP4Error::Eccentricty); 269 | } 270 | 271 | // sgp4fix fix tolerance to avoid a divide by zero 272 | if em < 1.0e-6 { 273 | em = 1.0e-6; 274 | } 275 | mm += satrec.no * templ; 276 | xlm = mm + argpm + nodem; 277 | 278 | nodem %= TWO_PI; 279 | argpm %= TWO_PI; 280 | xlm %= TWO_PI; 281 | mm = (xlm - argpm - nodem) % TWO_PI; 282 | 283 | // ----------------- compute extra mean quantities ------------- 284 | let sinim = inclm.sin(); 285 | let cosim = inclm.cos(); 286 | 287 | // -------------------- add lunar-solar periodics -------------- 288 | let ep = em; 289 | xincp = inclm; 290 | argpp = argpm; 291 | nodep = nodem; 292 | mp = mm; 293 | sinip = sinim; 294 | cosip = cosim; 295 | if satrec.method == InitlMethod::D { 296 | let dpper_parameters = DpperOptions { 297 | inclo: satrec.inclo, 298 | init: DpperInit::N, 299 | ep, 300 | inclp: xincp, 301 | nodep, 302 | argpp, 303 | mp, 304 | opsmode: satrec.operationmode.clone(), 305 | }; 306 | 307 | let dpper_result = dpper(&satrec, dpper_parameters); 308 | let DpperResult { 309 | ep, 310 | mut nodep, 311 | mut argpp, 312 | mp: _, 313 | .. 314 | } = dpper_result; 315 | 316 | xincp = dpper_result.inclp; 317 | 318 | if xincp < 0.0 { 319 | xincp = -xincp; 320 | nodep += PI; 321 | argpp -= PI; 322 | } 323 | if ep < 0.0 || ep > 1.0 { 324 | // printf("// error ep %f\n", ep); 325 | satrec.error = 3; 326 | // sgp4fix add return 327 | // return [false, false]; 328 | // panic!("error 3"); 329 | return Err(SGP4Error::Ep); 330 | } 331 | } 332 | 333 | // -------------------- long period periodics ------------------ 334 | if satrec.method == InitlMethod::D { 335 | sinip = xincp.sin(); 336 | cosip = xincp.cos(); 337 | satrec.aycof = -0.5 * J3OJ2 * sinip; 338 | 339 | // sgp4fix for divide by zero for xincp = 180 deg 340 | if (cosip + 1.0).abs() > 1.5e-12 { 341 | satrec.xlcof = (-0.25 * J3OJ2 * sinip * (3.0 + (5.0 * cosip))) / (1.0 + cosip); 342 | } else { 343 | satrec.xlcof = (-0.25 * J3OJ2 * sinip * (3.0 + (5.0 * cosip))) / TEMP4; 344 | } 345 | } 346 | 347 | let axnl = ep * argpp.cos(); 348 | temp = 1.0 / (am * (1.0 - (ep * ep))); 349 | let aynl = (ep * argpp.sin()) + (temp * satrec.aycof); 350 | let xl = mp + argpp + nodep + (temp * satrec.xlcof * axnl); 351 | 352 | // --------------------- solve kepler's equation --------------- 353 | let u = (xl - nodep) % TWO_PI; 354 | eo1 = u; 355 | tem5 = 9999.9; 356 | let mut ktr = 1; 357 | 358 | // sgp4fix for kepler iteration 359 | // the following iteration needs better limits on corrections 360 | while tem5.abs() >= 1.0e-12 && ktr <= 10 { 361 | sineo1 = eo1.sin(); 362 | coseo1 = eo1.cos(); 363 | tem5 = 1.0 - (coseo1 * axnl) - (sineo1 * aynl); 364 | tem5 = (((u - (aynl * coseo1)) + (axnl * sineo1)) - eo1) / tem5; 365 | if tem5.abs() >= 0.95 { 366 | if tem5 > 0.0 { 367 | tem5 = 0.95; 368 | } else { 369 | tem5 = -0.95; 370 | } 371 | } 372 | eo1 += tem5; 373 | ktr += 1; 374 | } 375 | 376 | // ------------- short period preliminary quantities ----------- 377 | let ecose = (axnl * coseo1) + (aynl * sineo1); 378 | let esine = (axnl * sineo1) - (aynl * coseo1); 379 | let el2 = (axnl * axnl) + (aynl * aynl); 380 | let pl = am * (1.0 - el2); 381 | if pl < 0.0 { 382 | // printf("// error pl %f\n", pl); 383 | satrec.error = 4; 384 | // sgp4fix add return 385 | // return [false, false]; 386 | // panic!("Error 4: pl: {}", pl); 387 | return Err(SGP4Error::Pl); 388 | } 389 | 390 | let rl = am * (1.0 - ecose); 391 | let rdotl = (am.sqrt() * esine) / rl; 392 | let rvdotl = pl.sqrt() / rl; 393 | let betal = (1.0 - el2).sqrt(); 394 | temp = esine / (1.0 + betal); 395 | let sinu = (am / rl) * (sineo1 - aynl - (axnl * temp)); 396 | let cosu = (am / rl) * ((coseo1 - axnl) + (aynl * temp)); 397 | su = sinu.atan2(cosu); 398 | let sin2u = (cosu + cosu) * sinu; 399 | let cos2u = 1.0 - (2.0 * sinu * sinu); 400 | temp = 1.0 / pl; 401 | let temp1 = 0.5 * J2 * temp; 402 | let temp2 = temp1 * temp; 403 | 404 | // -------------- update for short period periodics ------------ 405 | if satrec.method == InitlMethod::D { 406 | cosisq = cosip * cosip; 407 | satrec.con41 = (3.0 * cosisq) - 1.0; 408 | satrec.x1mth2 = 1.0 - cosisq; 409 | satrec.x7thm1 = (7.0 * cosisq) - 1.0; 410 | } 411 | 412 | let mrt = 413 | (rl * (1.0 - (1.5 * temp2 * betal * satrec.con41))) + (0.5 * temp1 * satrec.x1mth2 * cos2u); 414 | su -= 0.25 * temp2 * satrec.x7thm1 * sin2u; 415 | let xnode = nodep + (1.5 * temp2 * cosip * sin2u); 416 | let xinc = xincp + (1.5 * temp2 * cosip * sinip * cos2u); 417 | let mvt = rdotl - ((nm * temp1 * satrec.x1mth2 * sin2u) / XKE); 418 | let rvdot = rvdotl + ((nm * temp1 * ((satrec.x1mth2 * cos2u) + (1.5 * satrec.con41))) / XKE); 419 | 420 | // --------------------- orientation vectors ------------------- 421 | let sinsu = su.sin(); 422 | let cossu = su.cos(); 423 | let snod = xnode.sin(); 424 | let cnod = xnode.cos(); 425 | let sini = xinc.sin(); 426 | let cosi = xinc.cos(); 427 | let xmx = -snod * cosi; 428 | let xmy = cnod * cosi; 429 | let ux = (xmx * sinsu) + (cnod * cossu); 430 | let uy = (xmy * sinsu) + (snod * cossu); 431 | let uz = sini * sinsu; 432 | let vx = (xmx * cossu) - (cnod * sinsu); 433 | let vy = (xmy * cossu) - (snod * sinsu); 434 | let vz = sini * cossu; 435 | 436 | // --------- position and velocity (in km and km/sec) ---------- 437 | let r = Eci { 438 | x: (mrt * ux) * EARTH_RADIUS, 439 | y: (mrt * uy) * EARTH_RADIUS, 440 | z: (mrt * uz) * EARTH_RADIUS, 441 | }; 442 | let v = Vec3 { 443 | x: ((mvt * ux) + (rvdot * vx)) * vkmpersec, 444 | y: ((mvt * uy) + (rvdot * vy)) * vkmpersec, 445 | z: ((mvt * uz) + (rvdot * vz)) * vkmpersec, 446 | }; 447 | 448 | // sgp4fix for decaying satellites 449 | if mrt < 1.0 { 450 | // printf("// decay condition %11.6f \n",mrt); 451 | satrec.error = 6; 452 | // return { 453 | // position: false, 454 | // velocity: false, 455 | // }; 456 | 457 | // panic!("decay condition: mrt={}, (err 6)", mrt); 458 | 459 | return Err(SGP4Error::DecayCondition); 460 | } 461 | 462 | Ok(SGP4Result { 463 | position: r, 464 | velocity: v, 465 | }) 466 | } 467 | -------------------------------------------------------------------------------- /src/propogation/dpper.rs: -------------------------------------------------------------------------------- 1 | use crate::io::Satrec; 2 | use std::f64::consts::PI; 3 | 4 | const TWO_PI: f64 = 2.0 * PI; 5 | 6 | pub struct DpperResult { 7 | pub ep: f64, 8 | pub inclp: f64, 9 | pub nodep: f64, 10 | pub argpp: f64, 11 | pub mp: f64, 12 | } 13 | 14 | #[derive(PartialEq, Clone)] 15 | pub enum DpperInit { 16 | Y, 17 | N, 18 | } 19 | 20 | #[derive(PartialEq, Clone)] 21 | pub enum DpperOpsMode { 22 | A, 23 | I, 24 | UNDEFINED, 25 | } 26 | 27 | pub struct DpperOptions { 28 | pub init: DpperInit, 29 | pub opsmode: DpperOpsMode, 30 | 31 | pub inclo: f64, 32 | pub ep: f64, 33 | pub inclp: f64, 34 | pub nodep: f64, 35 | pub argpp: f64, 36 | pub mp: f64, 37 | } 38 | 39 | /* ----------------------------------------------------------------------------- 40 | * 41 | * procedure dpper 42 | * 43 | * this procedure provides deep space long period periodic contributions 44 | * to the mean elements. by design, these periodics are zero at epoch. 45 | * this used to be dscom which included initialization, but it's really a 46 | * recurring function. 47 | * 48 | * author : david vallado 719-573-2600 28 jun 2005 49 | * 50 | * inputs : 51 | * e3 - 52 | * ee2 - 53 | * peo - 54 | * pgho - 55 | * pho - 56 | * pinco - 57 | * plo - 58 | * se2 , se3 , sgh2, sgh3, sgh4, sh2, sh3, si2, si3, sl2, sl3, sl4 - 59 | * t - 60 | * xh2, xh3, xi2, xi3, xl2, xl3, xl4 - 61 | * zmol - 62 | * zmos - 63 | * ep - eccentricity 0.0 - 1.0 64 | * inclo - inclination - needed for lyddane modification 65 | * nodep - right ascension of ascending node 66 | * argpp - argument of perigee 67 | * mp - mean anomaly 68 | * 69 | * outputs : 70 | * ep - eccentricity 0.0 - 1.0 71 | * inclp - inclination 72 | * nodep - right ascension of ascending node 73 | * argpp - argument of perigee 74 | * mp - mean anomaly 75 | * 76 | * locals : 77 | * alfdp - 78 | * betdp - 79 | * cosip , sinip , cosop , sinop , 80 | * dalf - 81 | * dbet - 82 | * dls - 83 | * f2, f3 - 84 | * pe - 85 | * pgh - 86 | * ph - 87 | * pinc - 88 | * pl - 89 | * sel , ses , sghl , sghs , shl , shs , sil , sinzf , sis , 90 | * sll , sls 91 | * xls - 92 | * xnoh - 93 | * zf - 94 | * zm - 95 | * 96 | * coupling : 97 | * none. 98 | * 99 | * references : 100 | * hoots, roehrich, norad spacetrack report #3 1980 101 | * hoots, norad spacetrack report #6 1986 102 | * hoots, schumacher and glover 2004 103 | * vallado, crawford, hujsak, kelso 2006 104 | ----------------------------------------------------------------------------*/ 105 | pub fn dpper(satrec: &Satrec, options: DpperOptions) -> DpperResult { 106 | let e3 = satrec.e3; 107 | let ee2 = satrec.ee2; 108 | let peo = satrec.peo; 109 | let pgho = satrec.pgho; 110 | let pho = satrec.pho; 111 | let pinco = satrec.pinco; 112 | let plo = satrec.plo; 113 | let se2 = satrec.se2; 114 | let se3 = satrec.se3; 115 | let sgh2 = satrec.sgh2; 116 | let sgh3 = satrec.sgh3; 117 | let sgh4 = satrec.sgh4; 118 | let sh2 = satrec.sh2; 119 | let sh3 = satrec.sh3; 120 | let si2 = satrec.si2; 121 | let si3 = satrec.si3; 122 | let sl2 = satrec.sl2; 123 | let sl3 = satrec.sl3; 124 | let sl4 = satrec.sl4; 125 | let t = satrec.t; 126 | let xgh2 = satrec.xgh2; 127 | let xgh3 = satrec.xgh3; 128 | let xgh4 = satrec.xgh4; 129 | let xh2 = satrec.xh2; 130 | let xh3 = satrec.xh3; 131 | let xi2 = satrec.xi2; 132 | let xi3 = satrec.xi3; 133 | let xl2 = satrec.xl2; 134 | let xl3 = satrec.xl3; 135 | let xl4 = satrec.xl4; 136 | let zmol = satrec.zmol; 137 | let zmos = satrec.zmos; 138 | 139 | // Copy satellite attributes into local variables for convenience 140 | // and symmetry in writing formulae. 141 | 142 | let mut alfdp; 143 | let mut betdp; 144 | let cosip; 145 | let sinip; 146 | let cosop; 147 | let sinop; 148 | let dalf; 149 | let dbet; 150 | let dls; 151 | let mut f2; 152 | let mut f3; 153 | let mut pe; 154 | let mut pgh; 155 | let mut ph; 156 | let mut pinc; 157 | let mut pl; 158 | let mut sinzf; 159 | let mut xls; 160 | let xnoh; 161 | let mut zf; 162 | let mut zm; 163 | 164 | let init = options.init; 165 | let opsmode = options.opsmode; 166 | let mut ep = options.ep; 167 | let mut inclp = options.inclp; 168 | let mut nodep = options.nodep; 169 | let mut argpp = options.argpp; 170 | let mut mp = options.mp; 171 | 172 | // ---------------------- constants ----------------------------- 173 | const ZNS: f64 = 1.19459e-5; 174 | const ZES: f64 = 0.01675; 175 | const ZNL: f64 = 1.5835218e-4; 176 | const ZEL: f64 = 0.05490; 177 | 178 | // --------------- calculate time varying periodics ----------- 179 | zm = zmos + (ZNS * t); 180 | 181 | // be sure that the initial call has time set to zero 182 | if init == DpperInit::Y { 183 | zm = zmos; 184 | } 185 | 186 | zf = zm + (2.0 * ZES * zm.sin()); 187 | sinzf = zf.sin(); 188 | f2 = (0.5 * sinzf * sinzf) - 0.25; 189 | f3 = -0.5 * sinzf * zf.cos(); 190 | 191 | let ses = (se2 * f2) + (se3 * f3); 192 | let sis = (si2 * f2) + (si3 * f3); 193 | let sls = (sl2 * f2) + (sl3 * f3) + (sl4 * sinzf); 194 | let sghs = (sgh2 * f2) + (sgh3 * f3) + (sgh4 * sinzf); 195 | let shs = (sh2 * f2) + (sh3 * f3); 196 | 197 | zm = zmol + (ZNL * t); 198 | 199 | if init == DpperInit::Y { 200 | zm = zmol; 201 | } 202 | 203 | zf = zm + (2.0 * ZEL * zm.sin()); 204 | sinzf = zf.sin(); 205 | f2 = (0.5 * sinzf * sinzf) - 0.25; 206 | f3 = -0.5 * sinzf * zf.cos(); 207 | 208 | let sel = (ee2 * f2) + (e3 * f3); 209 | let sil = (xi2 * f2) + (xi3 * f3); 210 | let sll = (xl2 * f2) + (xl3 * f3) + (xl4 * sinzf); 211 | let sghl = (xgh2 * f2) + (xgh3 * f3) + (xgh4 * sinzf); 212 | let shll = (xh2 * f2) + (xh3 * f3); 213 | 214 | pe = ses + sel; 215 | pinc = sis + sil; 216 | pl = sls + sll; 217 | pgh = sghs + sghl; 218 | ph = shs + shll; 219 | 220 | if init == DpperInit::N { 221 | pe -= peo; 222 | pinc -= pinco; 223 | pl -= plo; 224 | pgh -= pgho; 225 | ph -= pho; 226 | inclp += pinc; 227 | ep += pe; 228 | sinip = inclp.sin(); 229 | cosip = inclp.cos(); 230 | 231 | /* ----------------- apply periodics directly ------------ */ 232 | // sgp4fix for lyddane choice 233 | // strn3 used original inclination - this is technically feasible 234 | // gsfc used perturbed inclination - also technically feasible 235 | // probably best to readjust the 0.2 limit value and limit discontinuity 236 | // 0.2 rad = 11.45916 deg 237 | // use next line for original strn3 approach and original inclination 238 | // if (inclo >= 0.2) 239 | // use next line for gsfc version and perturbed inclination 240 | if inclp >= 0.2 { 241 | ph /= sinip; 242 | pgh -= cosip * ph; 243 | argpp += pgh; 244 | nodep += ph; 245 | mp += pl; 246 | } else { 247 | // ---- apply periodics with lyddane modification ---- 248 | sinop = nodep.sin(); 249 | cosop = nodep.cos(); 250 | alfdp = sinip * sinop; 251 | betdp = sinip * cosop; 252 | dalf = (ph * cosop) + (pinc * cosip * sinop); 253 | dbet = (-ph * sinop) + (pinc * cosip * cosop); 254 | alfdp += dalf; 255 | betdp += dbet; 256 | nodep %= TWO_PI; 257 | 258 | // sgp4fix for afspc written intrinsic functions 259 | // nodep used without a trigonometric function ahead 260 | if nodep < 0.0 && opsmode == DpperOpsMode::A { 261 | nodep += TWO_PI; 262 | } 263 | xls = mp + argpp + (cosip * nodep); 264 | dls = (pl + pgh) - (pinc * nodep * sinip); 265 | xls += dls; 266 | xnoh = nodep; 267 | nodep = alfdp.atan2(betdp); 268 | 269 | // sgp4fix for afspc written intrinsic functions 270 | // nodep used without a trigonometric function ahead 271 | if nodep < 0.0 && opsmode == DpperOpsMode::A { 272 | nodep += TWO_PI; 273 | } 274 | if (xnoh - nodep).abs() > std::f64::consts::PI { 275 | if nodep < xnoh { 276 | nodep += TWO_PI; 277 | } else { 278 | nodep -= TWO_PI; 279 | } 280 | } 281 | mp += pl; 282 | argpp = xls - mp - (cosip * nodep); 283 | } 284 | } 285 | 286 | DpperResult { 287 | ep, 288 | inclp, 289 | nodep, 290 | argpp, 291 | mp, 292 | } 293 | } 294 | 295 | #[cfg(test)] 296 | mod test { 297 | use crate::io::Satrec; 298 | use crate::propogation::dpper::*; 299 | use crate::propogation::initl::*; 300 | use crate::tests::assert_similar; 301 | 302 | #[test] 303 | fn test_ds() { 304 | let satrec = Satrec { 305 | name: None, 306 | error: 0, 307 | satnum: "11801".to_string(), 308 | epochyr: 80, 309 | epochdays: 230.29629788, 310 | ndot: 4.3363644592306274e-8, 311 | nddot: 0.0, 312 | bstar: 0.014311, 313 | inclo: 0.8166674822761788, 314 | nodeo: 4.021856443150141, 315 | ecco: 0.7318036, 316 | argpo: 0.8285461931652521, 317 | mo: 0.1817184457298936, 318 | no: 0.009971131594572634, 319 | a: 3.817121025708788, 320 | alta: 5.610503933958173, 321 | altp: 0.023738117459404462, 322 | jdsatepoch: 2444468.79629788, 323 | isimp: 1, 324 | method: InitlMethod::D, 325 | aycof: 0.0008523715456365274, 326 | con41: 0.40625317982989756, 327 | cc1: 0.000002456092742119515, 328 | cc4: 0.0001111232746219671, 329 | cc5: 0.01667847920486393, 330 | d2: 0.0, 331 | d3: 0.0, 332 | d4: 0.0, 333 | delmo: 7.753547406313254, 334 | eta: 0.9956413449218073, 335 | argpdot: 0.0000034607723715772176, 336 | omgcof: 7.166665721514533e-18, 337 | sinmao: 0.1807199902443773, 338 | t: 0.0, 339 | t2cof: 0.0000036841391131792723, 340 | t3cof: 0.0, 341 | t4cof: 0.0, 342 | t5cof: 0.0, 343 | x1mth2: 0.5312489400567009, 344 | x7thm1: 2.281257419603094, 345 | mdot: 0.009971844927637492, 346 | nodedot: -0.0000035284989537160483, 347 | xlcof: 0.0016249664763650373, 348 | xmcof: -5.859015475500135e-13, 349 | nodecf: -1.4081043481329519e-11, 350 | irez: 0.0, 351 | d2201: 0.0, 352 | d2211: 0.0, 353 | d3210: 0.0, 354 | d3222: 0.0, 355 | d4410: 0.0, 356 | d4422: 0.0, 357 | d5220: 0.0, 358 | d5232: 0.0, 359 | d5421: 0.0, 360 | d5433: 0.0, 361 | dedt: 0.0, 362 | del1: 0.0, 363 | del2: 0.0, 364 | del3: 0.0, 365 | didt: 0.0, 366 | dmdt: 0.0, 367 | dnodt: 0.0, 368 | domdt: 0.0, 369 | e3: -0.00007499513323066247, 370 | ee2: 0.0003984687913511968, 371 | peo: 0.0, 372 | pgho: 0.0, 373 | pho: 0.0, 374 | pinco: 0.0, 375 | plo: 0.0, 376 | se2: -0.0023592253136306925, 377 | se3: -0.00007047176334622737, 378 | sgh2: -0.00018225669552040974, 379 | sgh3: -0.0022130294594592042, 380 | sgh4: -0.00006154293820943018, 381 | sh2: -0.0011394073362677001, 382 | sh3: 0.002509518064658255, 383 | si2: -0.00005208303756792119, 384 | si3: 0.0032873534354906702, 385 | sl2: 0.0027816840121748848, 386 | sl3: 0.0033383632815548628, 387 | sl4: 0.00025906770677081676, 388 | gsto: 1.265125075734467, 389 | xfact: 0.0, 390 | xgh2: 0.0001510256023997251, 391 | xgh3: 0.0003555337415001366, 392 | xgh4: -0.00003239876027006408, 393 | xh2: 0.00011285895673523819, 394 | xh3: -0.0004733943404491607, 395 | xi2: -0.00006414087517640146, 396 | xi3: -0.0005695169725370441, 397 | xl2: -0.0007034864707310533, 398 | xl3: -0.0005240671434151523, 399 | xl4: 0.00013638400715968452, 400 | xlamo: 0.0, 401 | zmol: 3.5674683899705713, 402 | zmos: 3.896090412268542, 403 | atime: 0.0, 404 | xli: 0.0, 405 | xni: 0.0, 406 | operationmode: DpperOpsMode::I, 407 | init: DpperInit::Y, 408 | }; 409 | 410 | let opts = DpperOptions { 411 | inclo: 0.8166674822761788, 412 | init: DpperInit::Y, 413 | ep: 0.7318036, 414 | inclp: 0.8166674822761788, 415 | nodep: 4.021856443150141, 416 | argpp: 0.8285461931652521, 417 | mp: 0.1817184457298936, 418 | opsmode: DpperOpsMode::I, 419 | }; 420 | 421 | let res = dpper(&satrec, opts); 422 | 423 | assert_eq!(res.ep, 0.7318036); 424 | assert_eq!(res.inclp, 0.8166674822761788); 425 | assert_eq!(res.nodep, 4.021856443150141); 426 | assert_eq!(res.argpp, 0.8285461931652521); 427 | assert_eq!(res.mp, 0.1817184457298936); 428 | } 429 | 430 | #[test] 431 | fn test_no_init() { 432 | let satrec = Satrec { 433 | name: None, 434 | error: 0, 435 | satnum: "11801".to_string(), 436 | epochyr: 80, 437 | epochdays: 230.29629788, 438 | ndot: 4.3363644592306274e-8, 439 | nddot: 0.0, 440 | bstar: 0.014311, 441 | inclo: 0.8166674822761788, 442 | nodeo: 4.021856443150141, 443 | ecco: 0.7318036, 444 | argpo: 0.8285461931652521, 445 | mo: 0.1817184457298936, 446 | no: 0.009971131594572634, 447 | a: 3.817121025708788, 448 | alta: 5.610503933958173, 449 | altp: 0.023738117459404462, 450 | jdsatepoch: 2444468.79629788, 451 | isimp: 1, 452 | method: InitlMethod::D, 453 | aycof: 0.0008518061660986528, 454 | con41: 0.40836674874013124, 455 | cc1: 0.000002456092742119515, 456 | cc4: 0.0001111232746219671, 457 | cc5: 0.01667847920486393, 458 | d2: 0.0, 459 | d3: 0.0, 460 | d4: 0.0, 461 | delmo: 7.753547406313254, 462 | eta: 0.9956413449218073, 463 | argpdot: 0.0000034607723715772176, 464 | omgcof: 7.166665721514533e-18, 465 | sinmao: 0.1807199902443773, 466 | t: 0.0, 467 | t2cof: 0.0000036841391131792723, 468 | t3cof: 0.0, 469 | t4cof: 0.0, 470 | t5cof: 0.0, 471 | x1mth2: 0.5305444170866229, 472 | x7thm1: 2.2861890803936395, 473 | mdot: 0.009971844927637492, 474 | nodedot: -0.0000035284989537160483, 475 | xlcof: 0.0016240429516640184, 476 | xmcof: -5.859015475500135e-13, 477 | nodecf: -1.4081043481329519e-11, 478 | irez: 0.0, 479 | d2201: 0.0, 480 | d2211: 0.0, 481 | d3210: 0.0, 482 | d3222: 0.0, 483 | d4410: 0.0, 484 | d4422: 0.0, 485 | d5220: 0.0, 486 | d5232: 0.0, 487 | d5421: 0.0, 488 | d5433: 0.0, 489 | dedt: 2.63860646954029e-8, 490 | del1: 0.0, 491 | del2: 0.0, 492 | del3: 0.0, 493 | didt: -3.4767374233712414e-8, 494 | dmdt: 8.037814266648781e-8, 495 | dnodt: -6.033631312091549e-8, 496 | domdt: 9.465204025716937e-9, 497 | e3: -0.00007499513323066247, 498 | ee2: 0.0003984687913511968, 499 | peo: 0.0, 500 | pgho: 0.0, 501 | pho: 0.0, 502 | pinco: 0.0, 503 | plo: 0.0, 504 | se2: -0.0023592253136306925, 505 | se3: -0.00007047176334622737, 506 | sgh2: -0.00018225669552040974, 507 | sgh3: -0.0022130294594592042, 508 | sgh4: -0.00006154293820943018, 509 | sh2: -0.0011394073362677001, 510 | sh3: 0.002509518064658255, 511 | si2: -0.00005208303756792119, 512 | si3: 0.0032873534354906702, 513 | sl2: 0.0027816840121748848, 514 | sl3: 0.0033383632815548628, 515 | sl4: 0.00025906770677081676, 516 | gsto: 1.265125075734467, 517 | xfact: 0.0, 518 | xgh2: 0.0001510256023997251, 519 | xgh3: 0.0003555337415001366, 520 | xgh4: -0.00003239876027006408, 521 | xh2: 0.00011285895673523819, 522 | xh3: -0.0004733943404491607, 523 | xi2: -0.00006414087517640146, 524 | xi3: -0.0005695169725370441, 525 | xl2: -0.0007034864707310533, 526 | xl3: -0.0005240671434151523, 527 | xl4: 0.00013638400715968452, 528 | xlamo: 0.0, 529 | zmol: 3.5674683899705713, 530 | zmos: 3.896090412268542, 531 | atime: 0.0, 532 | xli: 0.0, 533 | xni: 0.0, 534 | operationmode: DpperOpsMode::I, 535 | init: DpperInit::N, 536 | }; 537 | 538 | let opts = DpperOptions { 539 | inclo: 0.8166674822761788, 540 | init: DpperInit::N, 541 | ep: 0.7318036, 542 | inclp: 0.8166674822761788, 543 | nodep: 4.021856443150141, 544 | argpp: 0.8285461931652521, 545 | mp: 0.18171844572989393, 546 | opsmode: DpperOpsMode::UNDEFINED, // TODO: satellite-js has undefined here. 547 | }; 548 | 549 | let res = dpper(&satrec, opts); 550 | 551 | assert_eq!(res.ep, 0.7318253048776667); 552 | assert_eq!(res.inclp, 0.8159616103005034); 553 | assert_eq!(res.nodep, 4.02112614477864); 554 | assert_eq!(res.argpp, 0.829566024830811); 555 | assert_similar(res.mp, 0.1808079384635203); 556 | } 557 | } 558 | -------------------------------------------------------------------------------- /src/propogation/dscom.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | 3 | pub struct DscomOptions { 4 | pub epoch: f64, 5 | pub ep: f64, 6 | pub argpp: f64, 7 | pub tc: f64, 8 | pub inclp: f64, 9 | pub nodep: f64, 10 | pub np: f64, 11 | } 12 | 13 | pub struct DscomResult { 14 | pub snodm: f64, 15 | pub cnodm: f64, 16 | pub sinim: f64, 17 | pub cosim: f64, 18 | pub sinomm: f64, 19 | pub cosomm: f64, 20 | pub day: f64, 21 | pub e3: f64, 22 | pub ee2: f64, 23 | pub em: f64, 24 | pub emsq: f64, 25 | pub gam: f64, 26 | pub peo: f64, 27 | pub pgho: f64, 28 | pub pho: f64, 29 | pub pinco: f64, 30 | pub plo: f64, 31 | pub rtemsq: f64, 32 | pub se2: f64, 33 | pub se3: f64, 34 | pub sgh2: f64, 35 | pub sgh3: f64, 36 | pub sgh4: f64, 37 | pub sh2: f64, 38 | pub sh3: f64, 39 | pub si2: f64, 40 | pub si3: f64, 41 | pub sl2: f64, 42 | pub sl3: f64, 43 | pub sl4: f64, 44 | pub s1: f64, 45 | pub s2: f64, 46 | pub s3: f64, 47 | pub s4: f64, 48 | pub s5: f64, 49 | pub s6: f64, 50 | pub s7: f64, 51 | pub ss1: f64, 52 | pub ss2: f64, 53 | pub ss3: f64, 54 | pub ss4: f64, 55 | pub ss5: f64, 56 | pub ss6: f64, 57 | pub ss7: f64, 58 | pub sz1: f64, 59 | pub sz2: f64, 60 | pub sz3: f64, 61 | pub sz11: f64, 62 | pub sz12: f64, 63 | pub sz13: f64, 64 | pub sz21: f64, 65 | pub sz22: f64, 66 | pub sz23: f64, 67 | pub sz31: f64, 68 | pub sz32: f64, 69 | pub sz33: f64, 70 | pub xgh2: f64, 71 | pub xgh3: f64, 72 | pub xgh4: f64, 73 | pub xh2: f64, 74 | pub xh3: f64, 75 | pub xi2: f64, 76 | pub xi3: f64, 77 | pub xl2: f64, 78 | pub xl3: f64, 79 | pub xl4: f64, 80 | pub nm: f64, 81 | pub z1: f64, 82 | pub z2: f64, 83 | pub z3: f64, 84 | pub z11: f64, 85 | pub z12: f64, 86 | pub z13: f64, 87 | pub z21: f64, 88 | pub z22: f64, 89 | pub z23: f64, 90 | pub z31: f64, 91 | pub z32: f64, 92 | pub z33: f64, 93 | pub zmol: f64, 94 | pub zmos: f64, 95 | } 96 | 97 | /*----------------------------------------------------------------------------- 98 | * 99 | * procedure dscom 100 | * 101 | * this procedure provides deep space common items used by both the secular 102 | * and periodics subroutines. input is provided as shown. this routine 103 | * used to be called dpper, but the functions inside weren't well organized. 104 | * 105 | * author : david vallado 719-573-2600 28 jun 2005 106 | * 107 | * inputs : 108 | * epoch - 109 | * ep - eccentricity 110 | * argpp - argument of perigee 111 | * tc - 112 | * inclp - inclination 113 | * nodep - right ascension of ascending node 114 | * np - mean motion 115 | * 116 | * outputs : 117 | * sinim , cosim , sinomm , cosomm , snodm , cnodm 118 | * day - 119 | * e3 - 120 | * ee2 - 121 | * em - eccentricity 122 | * emsq - eccentricity squared 123 | * gam - 124 | * peo - 125 | * pgho - 126 | * pho - 127 | * pinco - 128 | * plo - 129 | * rtemsq - 130 | * se2, se3 - 131 | * sgh2, sgh3, sgh4 - 132 | * sh2, sh3, si2, si3, sl2, sl3, sl4 - 133 | * s1, s2, s3, s4, s5, s6, s7 - 134 | * ss1, ss2, ss3, ss4, ss5, ss6, ss7, sz1, sz2, sz3 - 135 | * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 - 136 | * xgh2, xgh3, xgh4, xh2, xh3, xi2, xi3, xl2, xl3, xl4 - 137 | * nm - mean motion 138 | * z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33 - 139 | * zmol - 140 | * zmos - 141 | * 142 | * locals : 143 | * a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 - 144 | * betasq - 145 | * cc - 146 | * ctem, stem - 147 | * x1, x2, x3, x4, x5, x6, x7, x8 - 148 | * xnodce - 149 | * xnoi - 150 | * zcosg , zsing , zcosgl , zsingl , zcosh , zsinh , zcoshl , zsinhl , 151 | * zcosi , zsini , zcosil , zsinil , 152 | * zx - 153 | * zy - 154 | * 155 | * coupling : 156 | * none. 157 | * 158 | * references : 159 | * hoots, roehrich, norad spacetrack report #3 1980 160 | * hoots, norad spacetrack report #6 1986 161 | * hoots, schumacher and glover 2004 162 | * vallado, crawford, hujsak, kelso 2006 163 | ----------------------------------------------------------------------------*/ 164 | pub fn dscom(options: DscomOptions) -> DscomResult { 165 | let mut a1; 166 | let mut a2; 167 | let mut a3; 168 | let mut a4; 169 | let mut a5; 170 | let mut a6; 171 | let mut a7; 172 | let mut a8; 173 | let mut a9; 174 | let mut a10; 175 | let mut cc; 176 | let mut x1; 177 | let mut x2; 178 | let mut x3; 179 | let mut x4; 180 | let mut x5; 181 | let mut x6; 182 | let mut x7; 183 | let mut x8; 184 | let mut zcosg; 185 | let mut zsing; 186 | let mut zcosh; 187 | let mut zsinh; 188 | let mut zcosi; 189 | let mut zsini; 190 | let mut ss1 = 0.0; 191 | let mut ss2 = 0.0; 192 | let mut ss3 = 0.0; 193 | let mut ss4 = 0.0; 194 | let mut ss5 = 0.0; // TODO: unused? 195 | let mut ss6 = 0.0; 196 | let mut ss7 = 0.0; 197 | let mut sz1 = 0.0; 198 | let mut sz2 = 0.0; 199 | let mut sz3 = 0.0; 200 | let mut sz11 = 0.0; 201 | let mut sz12 = 0.0; 202 | let mut sz13 = 0.0; 203 | let mut sz21 = 0.0; 204 | let mut sz22 = 0.0; 205 | let mut sz23 = 0.0; 206 | let mut sz31 = 0.0; 207 | let mut sz32 = 0.0; 208 | let mut sz33 = 0.0; 209 | let mut s1 = 0.0; 210 | let mut s2 = 0.0; 211 | let mut s3 = 0.0; 212 | let mut s4 = 0.0; 213 | let mut s5 = 0.0; // TODO: unused? 214 | let mut s6 = 0.0; 215 | let mut s7 = 0.0; 216 | let mut z1 = 0.0; 217 | let mut z2 = 0.0; 218 | let mut z3 = 0.0; 219 | let mut z11 = 0.0; 220 | let mut z12 = 0.0; 221 | let mut z13 = 0.0; 222 | let mut z21 = 0.0; 223 | let mut z22 = 0.0; 224 | let mut z23 = 0.0; 225 | let mut z31 = 0.0; 226 | let mut z32 = 0.0; 227 | let mut z33 = 0.0; 228 | 229 | // -------------------------- constants ------------------------- 230 | const ZES: f64 = 0.01675; 231 | const ZEL: f64 = 0.05490; 232 | const C1SS: f64 = 2.9864797e-6; 233 | const C1L: f64 = 4.7968065e-7; 234 | const ZSINIS: f64 = 0.39785416; 235 | const ZCOSIS: f64 = 0.91744867; 236 | const ZCOSGS: f64 = 0.1945905; 237 | const ZSINGS: f64 = -0.98088458; 238 | 239 | // --------------------- local variables ------------------------ 240 | let nm = options.np; 241 | let em = options.ep; 242 | let snodm = options.nodep.sin(); 243 | let cnodm = options.nodep.cos(); 244 | let sinomm = options.argpp.sin(); 245 | let cosomm = options.argpp.cos(); 246 | let sinim = options.inclp.sin(); 247 | let cosim = options.inclp.cos(); 248 | let emsq = em.powi(2); 249 | let betasq = 1.0 - emsq; 250 | let rtemsq = betasq.sqrt(); 251 | 252 | // ----------------- initialize lunar solar terms --------------- 253 | let peo = 0.0; 254 | let pinco = 0.0; 255 | let plo = 0.0; 256 | let pgho = 0.0; 257 | let pho = 0.0; 258 | let day = options.epoch + 18261.5 + (options.tc / 1440.0); 259 | let xnodce = (4.5236020 - (9.2422029e-4 * day)) % TWO_PI; 260 | let stem = xnodce.sin(); 261 | let ctem = xnodce.cos(); 262 | let zcosil = 0.91375164 - (0.03568096 * ctem); 263 | let zsinil = (1.0 - (zcosil * zcosil)).sqrt(); 264 | let zsinhl = (0.089683511 * stem) / zsinil; 265 | let zcoshl = (1.0 - (zsinhl * zsinhl)).sqrt(); 266 | let gam = 5.8351514 + (0.0019443680 * day); 267 | let mut zx = (0.39785416 * stem) / zsinil; 268 | let zy = (zcoshl * ctem) + (0.91744867 * zsinhl * stem); 269 | zx = zx.atan2(zy); 270 | zx += gam - xnodce; 271 | let zcosgl = zx.cos(); 272 | let zsingl = zx.sin(); 273 | 274 | // ------------------------- do solar terms --------------------- 275 | zcosg = ZCOSGS; 276 | zsing = ZSINGS; 277 | zcosi = ZCOSIS; 278 | zsini = ZSINIS; 279 | zcosh = cnodm; 280 | zsinh = snodm; 281 | cc = C1SS; 282 | let xnoi = 1.0 / nm; 283 | 284 | let mut lsflg = 0; 285 | while lsflg < 2 { 286 | lsflg += 1; 287 | a1 = (zcosg * zcosh) + (zsing * zcosi * zsinh); 288 | a3 = (-zsing * zcosh) + (zcosg * zcosi * zsinh); 289 | a7 = (-zcosg * zsinh) + (zsing * zcosi * zcosh); 290 | a8 = zsing * zsini; 291 | a9 = (zsing * zsinh) + (zcosg * zcosi * zcosh); 292 | a10 = zcosg * zsini; 293 | a2 = (cosim * a7) + (sinim * a8); 294 | a4 = (cosim * a9) + (sinim * a10); 295 | a5 = (-sinim * a7) + (cosim * a8); 296 | a6 = (-sinim * a9) + (cosim * a10); 297 | 298 | x1 = (a1 * cosomm) + (a2 * sinomm); 299 | x2 = (a3 * cosomm) + (a4 * sinomm); 300 | x3 = (-a1 * sinomm) + (a2 * cosomm); 301 | x4 = (-a3 * sinomm) + (a4 * cosomm); 302 | x5 = a5 * sinomm; 303 | x6 = a6 * sinomm; 304 | x7 = a5 * cosomm; 305 | x8 = a6 * cosomm; 306 | 307 | z31 = (12.0 * x1 * x1) - (3.0 * x3 * x3); 308 | z32 = (24.0 * x1 * x2) - (6.0 * x3 * x4); 309 | z33 = (12.0 * x2 * x2) - (3.0 * x4 * x4); 310 | 311 | z1 = (3.0 * ((a1 * a1) + (a2 * a2))) + (z31 * emsq); 312 | z2 = (6.0 * ((a1 * a3) + (a2 * a4))) + (z32 * emsq); 313 | z3 = (3.0 * ((a3 * a3) + (a4 * a4))) + (z33 * emsq); 314 | 315 | z11 = (-6.0 * a1 * a5) + (emsq * ((-24.0 * x1 * x7) - (6.0 * x3 * x5))); 316 | z12 = (-6.0 * ((a1 * a6) + (a3 * a5))) 317 | + (emsq * ((-24.0 * ((x2 * x7) + (x1 * x8))) + (-6.0 * ((x3 * x6) + (x4 * x5))))); 318 | 319 | z13 = (-6.0 * a3 * a6) + (emsq * ((-24.0 * x2 * x8) - (6.0 * x4 * x6))); 320 | 321 | z21 = (6.0 * a2 * a5) + (emsq * ((24.0 * x1 * x5) - (6.0 * x3 * x7))); 322 | z22 = (6.0 * ((a4 * a5) + (a2 * a6))) 323 | + (emsq * ((24.0 * ((x2 * x5) + (x1 * x6))) - (6.0 * ((x4 * x7) + (x3 * x8))))); 324 | z23 = (6.0 * a4 * a6) + (emsq * ((24.0 * x2 * x6) - (6.0 * x4 * x8))); 325 | 326 | z1 = z1 + z1 + (betasq * z31); 327 | z2 = z2 + z2 + (betasq * z32); 328 | z3 = z3 + z3 + (betasq * z33); 329 | s3 = cc * xnoi; 330 | s2 = (-0.5 * s3) / rtemsq; 331 | s4 = s3 * rtemsq; 332 | s1 = -15.0 * em * s4; 333 | s5 = (x1 * x3) + (x2 * x4); 334 | s6 = (x2 * x3) + (x1 * x4); 335 | s7 = (x2 * x4) - (x1 * x3); 336 | 337 | // ----------------------- do lunar terms ------------------- 338 | if lsflg == 1 { 339 | ss1 = s1; 340 | ss2 = s2; 341 | ss3 = s3; 342 | ss4 = s4; 343 | ss5 = s5; 344 | ss6 = s6; 345 | ss7 = s7; 346 | sz1 = z1; 347 | sz2 = z2; 348 | sz3 = z3; 349 | sz11 = z11; 350 | sz12 = z12; 351 | sz13 = z13; 352 | sz21 = z21; 353 | sz22 = z22; 354 | sz23 = z23; 355 | sz31 = z31; 356 | sz32 = z32; 357 | sz33 = z33; 358 | zcosg = zcosgl; 359 | zsing = zsingl; 360 | zcosi = zcosil; 361 | zsini = zsinil; 362 | zcosh = (zcoshl * cnodm) + (zsinhl * snodm); 363 | zsinh = (snodm * zcoshl) - (cnodm * zsinhl); 364 | cc = C1L; 365 | } 366 | } 367 | 368 | let zmol = (4.7199672 + ((0.22997150 * day) - gam)) % TWO_PI; 369 | let zmos = (6.2565837 + (0.017201977 * day)) % TWO_PI; 370 | 371 | // ------------------------ do solar terms ---------------------- 372 | let se2 = 2.0 * ss1 * ss6; 373 | let se3 = 2.0 * ss1 * ss7; 374 | let si2 = 2.0 * ss2 * sz12; 375 | let si3 = 2.0 * ss2 * (sz13 - sz11); 376 | let sl2 = -2.0 * ss3 * sz2; 377 | let sl3 = -2.0 * ss3 * (sz3 - sz1); 378 | let sl4 = -2.0 * ss3 * (-21.0 - (9.0 * emsq)) * ZES; 379 | let sgh2 = 2.0 * ss4 * sz32; 380 | let sgh3 = 2.0 * ss4 * (sz33 - sz31); 381 | let sgh4 = -18.0 * ss4 * ZES; 382 | let sh2 = -2.0 * ss2 * sz22; 383 | let sh3 = -2.0 * ss2 * (sz23 - sz21); 384 | 385 | // ------------------------ do lunar terms ---------------------- 386 | let ee2 = 2.0 * s1 * s6; 387 | let e3 = 2.0 * s1 * s7; 388 | let xi2 = 2.0 * s2 * z12; 389 | let xi3 = 2.0 * s2 * (z13 - z11); 390 | let xl2 = -2.0 * s3 * z2; 391 | let xl3 = -2.0 * s3 * (z3 - z1); 392 | let xl4 = -2.0 * s3 * (-21.0 - (9.0 * emsq)) * ZEL; 393 | let xgh2 = 2.0 * s4 * z32; 394 | let xgh3 = 2.0 * s4 * (z33 - z31); 395 | let xgh4 = -18.0 * s4 * ZEL; 396 | let xh2 = -2.0 * s2 * z22; 397 | let xh3 = -2.0 * s2 * (z23 - z21); 398 | 399 | DscomResult { 400 | snodm, 401 | cnodm, 402 | sinim, 403 | cosim, 404 | sinomm, 405 | 406 | cosomm, 407 | day, 408 | e3, 409 | ee2, 410 | em, 411 | 412 | emsq, 413 | gam, 414 | peo, 415 | pgho, 416 | pho, 417 | 418 | pinco, 419 | plo, 420 | rtemsq, 421 | se2, 422 | se3, 423 | 424 | sgh2, 425 | sgh3, 426 | sgh4, 427 | sh2, 428 | sh3, 429 | 430 | si2, 431 | si3, 432 | sl2, 433 | sl3, 434 | sl4, 435 | 436 | s1, 437 | s2, 438 | s3, 439 | s4, 440 | s5, 441 | 442 | s6, 443 | s7, 444 | ss1, 445 | ss2, 446 | ss3, 447 | 448 | ss4, 449 | ss5, 450 | ss6, 451 | ss7, 452 | sz1, 453 | 454 | sz2, 455 | sz3, 456 | sz11, 457 | sz12, 458 | sz13, 459 | 460 | sz21, 461 | sz22, 462 | sz23, 463 | sz31, 464 | sz32, 465 | 466 | sz33, 467 | xgh2, 468 | xgh3, 469 | xgh4, 470 | xh2, 471 | 472 | xh3, 473 | xi2, 474 | xi3, 475 | xl2, 476 | xl3, 477 | 478 | xl4, 479 | nm, 480 | z1, 481 | z2, 482 | z3, 483 | 484 | z11, 485 | z12, 486 | z13, 487 | z21, 488 | z22, 489 | 490 | z23, 491 | z31, 492 | z32, 493 | z33, 494 | zmol, 495 | 496 | zmos, 497 | } 498 | } 499 | 500 | #[cfg(test)] 501 | mod tests { 502 | use crate::propogation::dscom::*; 503 | use crate::tests::{assert_diff, assert_similar}; 504 | #[test] 505 | fn test() { 506 | let opts = DscomOptions { 507 | epoch: 11187.29629787989, 508 | ep: 0.7318036, 509 | argpp: 0.8285461931652521, 510 | tc: 0.0, 511 | inclp: 0.8166674822761788, 512 | nodep: 4.021856443150141, 513 | np: 0.009971131594572634, 514 | }; 515 | 516 | let res = dscom(opts); 517 | 518 | assert_similar(res.snodm, -0.7709069259013173); 519 | assert_similar(res.cnodm, -0.6369478091628709); 520 | assert_similar(res.sinim, 0.7288682597401953); 521 | assert_similar(res.cosim, 0.6846539709541596); 522 | assert_similar(res.sinomm, 0.7369494526341018); 523 | assert_similar(res.cosomm, 0.6759478561710938); 524 | assert_similar(res.day, 29448.79629787989); 525 | assert_similar(res.e3, -0.00007499513323066247); 526 | assert_similar(res.ee2, 0.0003984687913511968); 527 | assert_similar(res.em, 0.7318036); 528 | assert_similar(res.emsq, 0.53553650897296); 529 | assert_similar(res.gam, 63.09444856011612); 530 | assert_similar(res.peo, 0.0); 531 | assert_similar(res.pgho, 0.0); 532 | assert_similar(res.pho, 0.0); 533 | assert_similar(res.pinco, 0.0); 534 | assert_similar(res.plo, 0.0); 535 | assert_similar(res.rtemsq, 0.6815155838475302); 536 | assert_similar(res.se2, -0.0023592253136306925); 537 | assert_similar(res.se3, -0.00007047176334622737); 538 | assert_similar(res.sgh2, -0.00018225669552040974); 539 | assert_similar(res.sgh3, -0.0022130294594592042); 540 | assert_similar(res.sgh4, -0.00006154293820943018); 541 | assert_similar(res.sh2, -0.0011394073362677001); 542 | assert_similar(res.sh3, 0.002509518064658255); 543 | assert_similar(res.si2, -0.00005208303756792119); 544 | assert_similar(res.si3, 0.0032873534354906702); 545 | assert_similar(res.sl2, 0.0027816840121748848); 546 | assert_similar(res.sl3, 0.0033383632815548628); 547 | assert_similar(res.sl4, 0.00025906770677081676); 548 | assert_similar(res.s1, -0.0003598896387548552); 549 | assert_similar(res.s2, -0.000035294088067045225); 550 | assert_similar(res.s3, 0.00004810694207075695); 551 | assert_similar(res.s4, 0.000032785630712471235); 552 | assert_similar(res.s5, -0.32951417210709383); 553 | assert_similar(res.s6, -0.5535985875139634); 554 | assert_similar(res.s7, 0.10419184821509497); 555 | assert_similar(res.ss1, -0.0022406638674745552); 556 | assert_similar(res.ss2, -0.00021974010738653476); 557 | assert_similar(res.ss3, 0.00029951261516050645); 558 | assert_similar(res.ss4, 0.0002041225147908132); 559 | assert_similar(res.ss5, -0.2842063666667719); 560 | assert_similar(res.ss6, 0.5264567675404539); 561 | assert_similar(res.ss7, 0.015725643718630555); 562 | assert_diff(res.sz1, 7.241600464426519, 1e-14); 563 | assert_similar(res.sz2, -4.643684224593015); 564 | assert_similar(res.sz3, 1.6686076878687435); 565 | assert_diff(res.sz11, 5.923151674966536, 1e-14); 566 | assert_diff(res.sz12, 0.11851053999055416, 1e-14); 567 | assert_diff(res.sz13, -1.5569425931864267, 1e-14); 568 | assert_diff(res.sz21, -5.5489812856673435, 1e-14); 569 | assert_diff(res.sz22, -2.592624873581728, 1e-14); 570 | assert_diff(res.sz23, 0.16121448720509934, 1e-14); 571 | assert_diff(res.sz31, 3.273877043602411, 1e-14); 572 | assert_diff(res.sz32, -0.4464394721650089, 1e-14); 573 | assert_diff(res.sz33, -2.1469592167364815, 1e-14); 574 | assert_diff(res.xgh2, 0.0001510256023997251, 1e-14); 575 | assert_diff(res.xgh3, 0.0003555337415001366, 1e-14); 576 | assert_diff(res.xgh4, -0.00003239876027006408, 1e-14); 577 | assert_similar(res.xh2, 0.00011285895673523819); 578 | assert_similar(res.xh3, -0.0004733943404491607); 579 | assert_similar(res.xi2, -0.00006414087517640146); 580 | assert_similar(res.xi3, -0.0005695169725370441); 581 | assert_similar(res.xl2, -0.0007034864707310533); 582 | assert_similar(res.xl3, -0.0005240671434151523); 583 | assert_similar(res.xl4, 0.00013638400715968452); 584 | assert_similar(res.nm, 0.009971131594572634); 585 | assert_similar(res.z1, 2.5573881535383824); 586 | assert_diff(res.z2, 7.311693909959471, 1e-14); 587 | assert_diff(res.z3, 8.004285429240719, 1e-14); 588 | assert_similar(res.z11, -1.949045345610987); 589 | assert_diff(res.z12, 0.9086631598832984, 1e-14); 590 | assert_diff(res.z13, 6.119118527261723, 1e-14); 591 | assert_diff(res.z21, 0.6841370517901615, 1e-14); 592 | assert_diff(res.z22, 1.5988365604014116, 1e-14); 593 | assert_diff(res.z23, -6.022288391897364, 1e-14); 594 | assert_diff(res.z31, -1.633514023053113, 1e-14); 595 | assert_diff(res.z32, 2.3032285656514286, 1e-14); 596 | assert_diff(res.z33, 3.7885830019843842, 1e-14); 597 | assert_diff(res.zmol, 3.5674683899705713, 1e-14); 598 | assert_diff(res.zmos, 3.896090412268542, 1e-14); 599 | } 600 | } 601 | -------------------------------------------------------------------------------- /src/propogation/dsinit.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | 3 | pub struct DsinitOptions { 4 | pub cosim: f64, 5 | pub argpo: f64, 6 | pub s1: f64, 7 | pub s2: f64, 8 | pub s3: f64, 9 | pub s4: f64, 10 | pub s5: f64, 11 | pub sinim: f64, 12 | pub ss1: f64, 13 | pub ss2: f64, 14 | pub ss3: f64, 15 | pub ss4: f64, 16 | pub ss5: f64, 17 | pub sz1: f64, 18 | pub sz3: f64, 19 | pub sz11: f64, 20 | pub sz13: f64, 21 | pub sz21: f64, 22 | pub sz23: f64, 23 | pub sz31: f64, 24 | pub sz33: f64, 25 | pub t: f64, 26 | pub tc: f64, 27 | pub gsto: f64, 28 | pub mo: f64, 29 | pub mdot: f64, 30 | pub no: f64, 31 | pub nodeo: f64, 32 | pub nodedot: f64, 33 | pub xpidot: f64, 34 | pub z1: f64, 35 | pub z3: f64, 36 | pub z11: f64, 37 | pub z13: f64, 38 | pub z21: f64, 39 | pub z23: f64, 40 | pub z31: f64, 41 | pub z33: f64, 42 | pub ecco: f64, 43 | pub eccsq: f64, 44 | pub emsq: f64, 45 | pub em: f64, 46 | pub argpm: f64, 47 | pub inclm: f64, 48 | pub mm: f64, 49 | pub nm: f64, 50 | pub nodem: f64, 51 | pub irez: f64, 52 | pub atime: f64, 53 | pub d2201: f64, 54 | pub d2211: f64, 55 | pub d3210: f64, 56 | pub d3222: f64, 57 | pub d4410: f64, 58 | pub d4422: f64, 59 | pub d5220: f64, 60 | pub d5232: f64, 61 | pub d5421: f64, 62 | pub d5433: f64, 63 | pub dedt: f64, 64 | pub didt: f64, 65 | pub dmdt: f64, 66 | pub dnodt: f64, 67 | pub domdt: f64, 68 | pub del1: f64, 69 | pub del2: f64, 70 | pub del3: f64, 71 | pub xfact: f64, 72 | pub xlamo: f64, 73 | pub xli: f64, 74 | pub xni: f64, 75 | } 76 | 77 | pub struct DinitResult { 78 | pub em: f64, 79 | pub argpm: f64, 80 | pub inclm: f64, 81 | pub mm: f64, 82 | pub nm: f64, 83 | pub nodem: f64, 84 | pub irez: f64, 85 | pub atime: f64, 86 | pub d2201: f64, 87 | pub d2211: f64, 88 | pub d3210: f64, 89 | pub d3222: f64, 90 | pub d4410: f64, 91 | pub d4422: f64, 92 | pub d5220: f64, 93 | pub d5232: f64, 94 | pub d5421: f64, 95 | pub d5433: f64, 96 | pub dedt: f64, 97 | pub didt: f64, 98 | pub dmdt: f64, 99 | pub dndt: f64, 100 | pub dnodt: f64, 101 | pub domdt: f64, 102 | pub del1: f64, 103 | pub del2: f64, 104 | pub del3: f64, 105 | pub xfact: f64, 106 | pub xlamo: f64, 107 | pub xli: f64, 108 | pub xni: f64, 109 | } 110 | 111 | /*----------------------------------------------------------------------------- 112 | * 113 | * procedure dsinit 114 | * 115 | * this procedure provides deep space contributions to mean motion dot due 116 | * to geopotential resonance with half day and one day orbits. 117 | * 118 | * author : david vallado 719-573-2600 28 jun 2005 119 | * 120 | * inputs : 121 | * cosim, sinim- 122 | * emsq - eccentricity squared 123 | * argpo - argument of perigee 124 | * s1, s2, s3, s4, s5 - 125 | * ss1, ss2, ss3, ss4, ss5 - 126 | * sz1, sz3, sz11, sz13, sz21, sz23, sz31, sz33 - 127 | * t - time 128 | * tc - 129 | * gsto - greenwich sidereal time rad 130 | * mo - mean anomaly 131 | * mdot - mean anomaly dot (rate) 132 | * no - mean motion 133 | * nodeo - right ascension of ascending node 134 | * nodedot - right ascension of ascending node dot (rate) 135 | * xpidot - 136 | * z1, z3, z11, z13, z21, z23, z31, z33 - 137 | * eccm - eccentricity 138 | * argpm - argument of perigee 139 | * inclm - inclination 140 | * mm - mean anomaly 141 | * xn - mean motion 142 | * nodem - right ascension of ascending node 143 | * 144 | * outputs : 145 | * em - eccentricity 146 | * argpm - argument of perigee 147 | * inclm - inclination 148 | * mm - mean anomaly 149 | * nm - mean motion 150 | * nodem - right ascension of ascending node 151 | * irez - flag for resonance 0-none, 1-one day, 2-half day 152 | * atime - 153 | * d2201, d2211, d3210, d3222, d4410, d4422, d5220, d5232, d5421, d5433 - 154 | * dedt - 155 | * didt - 156 | * dmdt - 157 | * dndt - 158 | * dnodt - 159 | * domdt - 160 | * del1, del2, del3 - 161 | * ses , sghl , sghs , sgs , shl , shs , sis , sls 162 | * theta - 163 | * xfact - 164 | * xlamo - 165 | * xli - 166 | * xni 167 | * 168 | * locals : 169 | * ainv2 - 170 | * aonv - 171 | * cosisq - 172 | * eoc - 173 | * f220, f221, f311, f321, f322, f330, f441, f442, f522, f523, f542, f543 - 174 | * g200, g201, g211, g300, g310, g322, g410, g422, g520, g521, g532, g533 - 175 | * sini2 - 176 | * temp - 177 | * temp1 - 178 | * theta - 179 | * xno2 - 180 | * 181 | * coupling : 182 | * getgravconst 183 | * 184 | * references : 185 | * hoots, roehrich, norad spacetrack report #3 1980 186 | * hoots, norad spacetrack report #6 1986 187 | * hoots, schumacher and glover 2004 188 | * vallado, crawford, hujsak, kelso 2006 189 | ----------------------------------------------------------------------------*/ 190 | 191 | pub fn dsinit(options: DsinitOptions) -> DinitResult { 192 | let cosim = options.cosim; 193 | let argpo = options.argpo; 194 | let s1 = options.s1; 195 | let s2 = options.s2; 196 | let s3 = options.s3; 197 | let s4 = options.s4; 198 | let s5 = options.s5; 199 | let sinim = options.sinim; 200 | let ss1 = options.ss1; 201 | let ss2 = options.ss2; 202 | let ss3 = options.ss3; 203 | let ss4 = options.ss4; 204 | let ss5 = options.ss5; 205 | let sz1 = options.sz1; 206 | let sz3 = options.sz3; 207 | let sz11 = options.sz11; 208 | let sz13 = options.sz13; 209 | let sz21 = options.sz21; 210 | let sz23 = options.sz23; 211 | let sz31 = options.sz31; 212 | let sz33 = options.sz33; 213 | let t = options.t; 214 | let tc = options.tc; 215 | let gsto = options.gsto; 216 | let mo = options.mo; 217 | let mdot = options.mdot; 218 | let no = options.no; 219 | let nodeo = options.nodeo; 220 | let nodedot = options.nodedot; 221 | let xpidot = options.xpidot; 222 | let z1 = options.z1; 223 | let z3 = options.z3; 224 | let z11 = options.z11; 225 | let z13 = options.z13; 226 | let z21 = options.z21; 227 | let z23 = options.z23; 228 | let z31 = options.z31; 229 | let z33 = options.z33; 230 | let ecco = options.ecco; 231 | let eccsq = options.eccsq; 232 | let mut emsq = options.emsq; 233 | let mut em = options.em; 234 | let mut argpm = options.argpm; 235 | let mut inclm = options.inclm; 236 | let mut mm = options.mm; 237 | let mut nm = options.nm; 238 | let mut nodem = options.nodem; 239 | let mut irez = options.irez; 240 | let mut atime = options.atime; 241 | let mut d3222 = options.d3222; 242 | let mut d2201 = options.d2201; 243 | let mut d4422 = options.d4422; 244 | let mut d2211 = options.d2211; 245 | let mut d5232 = options.d5232; 246 | let mut d3210 = options.d3210; 247 | let mut d5433 = options.d5433; 248 | let mut d4410 = options.d4410; 249 | let mut didt = options.didt; 250 | let mut d5220 = options.d5220; 251 | let mut dnodt = options.dnodt; 252 | let mut d5421 = options.d5421; 253 | let mut del1 = options.del1; 254 | let mut dedt = options.dedt; 255 | let mut del3 = options.del3; 256 | let mut dmdt = options.dmdt; 257 | let mut xlamo = options.xlamo; 258 | let mut domdt = options.domdt; 259 | let mut xni = options.xni; 260 | let mut del2 = options.del2; 261 | let mut xfact = options.xfact; 262 | let mut xli = options.xli; 263 | 264 | let mut f220: f64; 265 | let f221: f64; 266 | let f311: f64; 267 | let f321: f64; 268 | let f322: f64; 269 | let mut f330: f64; 270 | let f441: f64; 271 | let f442: f64; 272 | let f522: f64; 273 | let f523: f64; 274 | let f542: f64; 275 | let f543: f64; 276 | let g200: f64; 277 | let g201: f64; 278 | let g211: f64; 279 | let g300: f64; 280 | let mut g310: f64; 281 | let g322: f64; 282 | let g410: f64; 283 | let g422: f64; 284 | let g520: f64; 285 | let g521: f64; 286 | let g532: f64; 287 | let g533: f64; 288 | let sini2: f64; 289 | let mut temp: f64; 290 | let mut temp1: f64; 291 | let xno2: f64; 292 | let ainv2: f64; 293 | let aonv: f64; 294 | let cosisq: f64; 295 | let eoc: f64; 296 | 297 | const Q22: f64 = 1.7891679e-6; 298 | const Q31: f64 = 2.1460748e-6; 299 | const Q33: f64 = 2.2123015e-7; 300 | const ROOT22: f64 = 1.7891679e-6; 301 | const ROOT44: f64 = 7.3636953e-9; 302 | const ROOT54: f64 = 2.1765803e-9; 303 | const RPTIM: f64 = 4.37526908801129966e-3; // equates to 7.29211514668855e-5 rad/sec 304 | const ROOT32: f64 = 3.7393792e-7; 305 | const ROOT52: f64 = 1.1428639e-7; 306 | const ZNL: f64 = 1.5835218e-4; 307 | const ZNS: f64 = 1.19459e-5; 308 | 309 | // -------------------- deep space initialization ------------ 310 | irez = 0.0; 311 | if (nm < 0.0052359877) && (nm > 0.0034906585) { 312 | irez = 1.0; 313 | } 314 | if (nm >= 8.26e-3) && (nm <= 9.24e-3) && (em >= 0.5) { 315 | irez = 2.0; 316 | } 317 | 318 | // ------------------------ do solar terms ------------------- 319 | let ses = ss1 * ZNS * ss5; 320 | let sis = ss2 * ZNS * (sz11 + sz13); 321 | let sls = -ZNS * ss3 * ((sz1 + sz3) - 14.0 - (6.0 * emsq)); 322 | let sghs = ss4 * ZNS * ((sz31 + sz33) - 6.0); 323 | let mut shs = -ZNS * ss2 * (sz21 + sz23); 324 | 325 | // sgp4fix for 180 deg incl 326 | if inclm < 5.2359877e-2 || inclm > PI - 5.2359877e-2 { 327 | shs = 0.0; 328 | } 329 | if sinim != 0.0 { 330 | shs /= sinim; 331 | } 332 | let sgs = sghs - (cosim * shs); 333 | 334 | // ------------------------- do lunar terms ------------------ 335 | dedt = ses + (s1 * ZNL * s5); 336 | didt = sis + (s2 * ZNL * (z11 + z13)); 337 | dmdt = sls - (ZNL * s3 * ((z1 + z3) - 14.0 - (6.0 * emsq))); 338 | let sghl = s4 * ZNL * ((z31 + z33) - 6.0); 339 | let mut shll = -ZNL * s2 * (z21 + z23); 340 | 341 | // sgp4fix for 180 deg incl 342 | if (inclm < 5.2359877e-2) || (inclm > (PI - 5.2359877e-2)) { 343 | shll = 0.0; 344 | } 345 | domdt = sgs + sghl; 346 | dnodt = shs; 347 | if sinim != 0.0 { 348 | domdt -= (cosim / sinim) * shll; 349 | dnodt += shll / sinim; 350 | } 351 | 352 | // ----------- calculate deep space resonance effects -------- 353 | let dndt = 0.0; 354 | let theta = (gsto + (tc * RPTIM)) % TWO_PI; 355 | em += dedt * t; 356 | inclm += didt * t; 357 | argpm += domdt * t; 358 | nodem += dnodt * t; 359 | mm += dmdt * t; 360 | 361 | // sgp4fix for negative inclinations 362 | // the following if statement should be commented out 363 | // if (inclm < 0.0) 364 | // { 365 | // inclm = -inclm; 366 | // argpm = argpm - pi; 367 | // nodem = nodem + pi; 368 | // } 369 | 370 | // -------------- initialize the resonance terms ------------- 371 | if irez != 0.0 { 372 | aonv = (nm / XKE).powf(X2O3); 373 | 374 | // ---------- geopotential resonance for 12 hour orbits ------ 375 | if irez == 2.0 { 376 | cosisq = cosim * cosim; 377 | let emo = em; 378 | em = ecco; 379 | let emsqo = emsq; 380 | emsq = eccsq; 381 | eoc = em * emsq; 382 | g201 = -0.306 - ((em - 0.64) * 0.440); 383 | 384 | if em <= 0.65 { 385 | g211 = (3.616 - (13.2470 * em)) + (16.2900 * emsq); 386 | g310 = ((-19.302 + (117.3900 * em)) - (228.4190 * emsq)) + (156.5910 * eoc); 387 | g322 = ((-18.9068 + (109.7927 * em)) - (214.6334 * emsq)) + (146.5816 * eoc); 388 | g410 = ((-41.122 + (242.6940 * em)) - (471.0940 * emsq)) + (313.9530 * eoc); 389 | g422 = ((-146.407 + (841.8800 * em)) - (1629.014 * emsq)) + (1083.4350 * eoc); 390 | g520 = ((-532.114 + (3017.977 * em)) - (5740.032 * emsq)) + (3708.2760 * eoc); 391 | } else { 392 | g211 = ((-72.099 + (331.819 * em)) - (508.738 * emsq)) + (266.724 * eoc); 393 | g310 = ((-346.844 + (1582.851 * em)) - (2415.925 * emsq)) + (1246.113 * eoc); 394 | g322 = ((-342.585 + (1554.908 * em)) - (2366.899 * emsq)) + (1215.972 * eoc); 395 | g410 = ((-1052.797 + (4758.686 * em)) - (7193.992 * emsq)) + (3651.957 * eoc); 396 | g422 = ((-3581.690 + (16178.110 * em)) - (24462.770 * emsq)) + (12422.520 * eoc); 397 | if em > 0.715 { 398 | g520 = ((-5149.66 + (29936.92 * em)) - (54087.36 * emsq)) + (31324.56 * eoc); 399 | } else { 400 | g520 = (1464.74 - (4664.75 * em)) + (3763.64 * emsq); 401 | } 402 | } 403 | if em < 0.7 { 404 | g533 = ((-919.22770 + (4988.6100 * em)) - (9064.7700 * emsq)) + (5542.21 * eoc); 405 | g521 = ((-822.71072 + (4568.6173 * em)) - (8491.4146 * emsq)) + (5337.524 * eoc); 406 | g532 = ((-853.66600 + (4690.2500 * em)) - (8624.7700 * emsq)) + (5341.4 * eoc); 407 | } else { 408 | g533 = ((-37995.780 + (161616.52 * em)) - (229838.20 * emsq)) + (109377.94 * eoc); 409 | g521 = ((-51752.104 + (218913.95 * em)) - (309468.16 * emsq)) + (146349.42 * eoc); 410 | g532 = ((-40023.880 + (170470.89 * em)) - (242699.48 * emsq)) + (115605.82 * eoc); 411 | } 412 | sini2 = sinim * sinim; 413 | f220 = 0.75 * (1.0 + (2.0 * cosim) + cosisq); 414 | f221 = 1.5 * sini2; 415 | f321 = 1.875 * sinim * (1.0 - (2.0 * cosim) - (3.0 * cosisq)); 416 | f322 = -1.875 * sinim * ((1.0 + (2.0 * cosim)) - (3.0 * cosisq)); 417 | f441 = 35.0 * sini2 * f220; 418 | f442 = 39.3750 * sini2 * sini2; 419 | 420 | f522 = 9.84375 421 | * sinim 422 | * ((sini2 * (1.0 - (2.0 * cosim) - (5.0 * cosisq))) 423 | + (0.33333333 * (-2.0 + (4.0 * cosim) + (6.0 * cosisq)))); 424 | f523 = sinim 425 | * ((4.92187512 * sini2 * ((-2.0 - (4.0 * cosim)) + (10.0 * cosisq))) 426 | + (6.56250012 * ((1.0 + (2.0 * cosim)) - (3.0 * cosisq)))); 427 | f542 = 29.53125 428 | * sinim 429 | * ((2.0 - (8.0 * cosim)) + (cosisq * (-12.0 + (8.0 * cosim) + (10.0 * cosisq)))); 430 | f543 = 29.53125 431 | * sinim 432 | * ((-2.0 - (8.0 * cosim)) + (cosisq * ((12.0 + (8.0 * cosim)) - (10.0 * cosisq)))); 433 | 434 | xno2 = nm * nm; 435 | ainv2 = aonv * aonv; 436 | temp1 = 3.0 * xno2 * ainv2; 437 | temp = temp1 * ROOT22; 438 | d2201 = temp * f220 * g201; 439 | d2211 = temp * f221 * g211; 440 | temp1 *= aonv; 441 | temp = temp1 * ROOT32; 442 | d3210 = temp * f321 * g310; 443 | d3222 = temp * f322 * g322; 444 | temp1 *= aonv; 445 | temp = 2.0 * temp1 * ROOT44; 446 | d4410 = temp * f441 * g410; 447 | d4422 = temp * f442 * g422; 448 | temp1 *= aonv; 449 | temp = temp1 * ROOT52; 450 | d5220 = temp * f522 * g520; 451 | d5232 = temp * f523 * g532; 452 | temp = 2.0 * temp1 * ROOT54; 453 | d5421 = temp * f542 * g521; 454 | d5433 = temp * f543 * g533; 455 | xlamo = ((mo + nodeo + nodeo) - (theta + theta)) % TWO_PI; 456 | xfact = (mdot + dmdt + (2.0 * ((nodedot + dnodt) - RPTIM))) - no; 457 | em = emo; 458 | emsq = emsqo; 459 | } 460 | 461 | // ---------------- synchronous resonance terms -------------- 462 | if irez == 1.0 { 463 | g200 = 1.0 + (emsq * (-2.5 + (0.8125 * emsq))); 464 | g310 = 1.0 + (2.0 * emsq); 465 | g300 = 1.0 + (emsq * (-6.0 + (6.60937 * emsq))); 466 | f220 = 0.75 * (1.0 + cosim) * (1.0 + cosim); 467 | f311 = (0.9375 * sinim * sinim * (1.0 + (3.0 * cosim))) - (0.75 * (1.0 + cosim)); 468 | f330 = 1.0 + cosim; 469 | f330 *= 1.875 * f330 * f330; 470 | del1 = 3.0 * nm * nm * aonv * aonv; 471 | del2 = 2.0 * del1 * f220 * g200 * Q22; 472 | del3 = 3.0 * del1 * f330 * g300 * Q33 * aonv; 473 | del1 = del1 * f311 * g310 * Q31 * aonv; 474 | xlamo = ((mo + nodeo + argpo) - theta) % TWO_PI; 475 | xfact = (mdot + xpidot + dmdt + domdt + dnodt) - (no + RPTIM); 476 | } 477 | 478 | // ------------ for sgp4, initialize the integrator ---------- 479 | xli = xlamo; 480 | xni = no; 481 | atime = 0.0; 482 | nm = no + dndt; 483 | } 484 | 485 | DinitResult { 486 | em, 487 | argpm, 488 | inclm, 489 | mm, 490 | nm, 491 | nodem, 492 | 493 | irez, 494 | atime, 495 | 496 | d2201, 497 | d2211, 498 | d3210, 499 | d3222, 500 | d4410, 501 | 502 | d4422, 503 | d5220, 504 | d5232, 505 | d5421, 506 | d5433, 507 | 508 | dedt, 509 | didt, 510 | dmdt, 511 | dndt, 512 | dnodt, 513 | domdt, 514 | 515 | del1, 516 | del2, 517 | del3, 518 | 519 | xfact, 520 | xlamo, 521 | xli, 522 | xni, 523 | } 524 | } 525 | 526 | #[cfg(test)] 527 | mod tests { 528 | use crate::propogation::dsinit::*; 529 | 530 | #[test] 531 | fn test_dsinit() { 532 | let opts = DsinitOptions { 533 | cosim: 0.6846539709541596, 534 | emsq: 0.53553650897296, 535 | argpo: 0.8285461931652521, 536 | s1: -0.0003598896387548552, 537 | s2: -0.000035294088067045225, 538 | s3: 0.00004810694207075695, 539 | s4: 0.000032785630712471235, 540 | s5: -0.32951417210709383, 541 | sinim: 0.7288682597401953, 542 | ss1: -0.0022406638674745552, 543 | ss2: -0.00021974010738653476, 544 | ss3: 0.00029951261516050645, 545 | ss4: 0.0002041225147908132, 546 | ss5: -0.2842063666667719, 547 | sz1: 7.241600464426519, 548 | sz3: 1.6686076878687435, 549 | sz11: 5.923151674966536, 550 | sz13: -1.5569425931864267, 551 | sz21: -5.5489812856673435, 552 | sz23: 0.16121448720509934, 553 | sz31: 3.273877043602411, 554 | sz33: -2.1469592167364815, 555 | t: 0.0, 556 | tc: 0.0, 557 | gsto: 1.265125075734467, 558 | mo: 0.1817184457298936, 559 | mdot: 0.009971844927637492, 560 | no: 0.009971131594572634, 561 | nodeo: 4.021856443150141, 562 | nodedot: -0.0000035284989537160483, 563 | xpidot: -6.772658213883069e-8, 564 | z1: 2.5573881535383824, 565 | z3: 8.004285429240719, 566 | z11: -1.949045345610987, 567 | z13: 6.119118527261723, 568 | z21: 0.6841370517901615, 569 | z23: -6.022288391897364, 570 | z31: -1.633514023053113, 571 | z33: 3.7885830019843842, 572 | ecco: 0.7318036, 573 | eccsq: 0.53553650897296, 574 | em: 0.7318036, 575 | argpm: 0.0, 576 | inclm: 0.8166674822761788, 577 | mm: 0.0, 578 | nm: 0.009971131594572634, 579 | nodem: 0.0, 580 | irez: 0.0, 581 | atime: 0.0, 582 | d2201: 0.0, 583 | d2211: 0.0, 584 | d3210: 0.0, 585 | d3222: 0.0, 586 | d4410: 0.0, 587 | d4422: 0.0, 588 | d5220: 0.0, 589 | d5232: 0.0, 590 | d5421: 0.0, 591 | d5433: 0.0, 592 | dedt: 0.0, 593 | didt: 0.0, 594 | dmdt: 0.0, 595 | dnodt: 0.0, 596 | domdt: 0.0, 597 | del1: 0.0, 598 | del2: 0.0, 599 | del3: 0.0, 600 | xfact: 0.0, 601 | xlamo: 0.0, 602 | xli: 0.0, 603 | xni: 0.0, 604 | }; 605 | 606 | let res = dsinit(opts); 607 | 608 | assert_eq!(res.em, 0.7318036); 609 | assert_eq!(res.argpm, 0.0); 610 | assert_eq!(res.inclm, 0.8166674822761788); 611 | assert_eq!(res.mm, 0.0); 612 | assert_eq!(res.nm, 0.009971131594572634); 613 | assert_eq!(res.nodem, 0.0); 614 | assert_eq!(res.irez, 0.0); 615 | assert_eq!(res.atime, 0.0); 616 | assert_eq!(res.d2201, 0.0); 617 | assert_eq!(res.d2211, 0.0); 618 | assert_eq!(res.d3210, 0.0); 619 | assert_eq!(res.d3222, 0.0); 620 | assert_eq!(res.d4410, 0.0); 621 | assert_eq!(res.d4422, 0.0); 622 | assert_eq!(res.d5220, 0.0); 623 | assert_eq!(res.d5232, 0.0); 624 | assert_eq!(res.d5421, 0.0); 625 | assert_eq!(res.d5433, 0.0); 626 | assert_eq!(res.dedt, 2.63860646954029e-8); 627 | assert_eq!(res.didt, -3.4767374233712414e-8); 628 | assert_eq!(res.dmdt, 8.037814266648781e-8); 629 | assert_eq!(res.dndt, 0.0); 630 | assert_eq!(res.dnodt, -6.033631312091549e-8); 631 | assert_eq!(res.domdt, 9.465204025716937e-9); 632 | assert_eq!(res.del1, 0.0); 633 | assert_eq!(res.del2, 0.0); 634 | assert_eq!(res.del3, 0.0); 635 | assert_eq!(res.xfact, 0.0); 636 | assert_eq!(res.xlamo, 0.0); 637 | assert_eq!(res.xli, 0.0); 638 | assert_eq!(res.xni, 0.0); 639 | } 640 | } 641 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | use crate::ext; 3 | use crate::propogation::dpper::*; 4 | use crate::propogation::initl::*; 5 | use crate::propogation::sgp4init; 6 | use crate::propogation::sgp4::*; 7 | 8 | #[derive(Clone)] 9 | pub struct Satrec { 10 | pub name: Option, 11 | pub init: DpperInit, 12 | pub operationmode: DpperOpsMode, 13 | pub error: u32, 14 | pub satnum: String, 15 | pub epochyr: i64, 16 | pub epochdays: f64, 17 | pub ndot: f64, 18 | pub nddot: f64, 19 | pub bstar: f64, 20 | pub inclo: f64, 21 | pub nodeo: f64, 22 | pub ecco: f64, 23 | pub mo: f64, 24 | pub no: f64, 25 | pub a: f64, 26 | pub argpo: f64, 27 | pub alta: f64, 28 | pub altp: f64, 29 | pub jdsatepoch: f64, 30 | 31 | // Near Earth Variables 32 | pub isimp: i64, 33 | pub method: InitlMethod, 34 | pub aycof: f64, 35 | pub con41: f64, 36 | pub cc1: f64, 37 | pub cc4: f64, 38 | pub cc5: f64, 39 | pub d2: f64, 40 | pub d3: f64, 41 | pub d4: f64, 42 | pub delmo: f64, 43 | pub eta: f64, 44 | pub argpdot: f64, 45 | pub omgcof: f64, 46 | pub sinmao: f64, 47 | pub t: f64, 48 | pub t2cof: f64, 49 | pub t3cof: f64, 50 | pub t4cof: f64, 51 | pub t5cof: f64, 52 | pub x1mth2: f64, 53 | pub x7thm1: f64, 54 | pub mdot: f64, 55 | pub nodedot: f64, 56 | pub xlcof: f64, 57 | pub xmcof: f64, 58 | pub nodecf: f64, 59 | 60 | // Deep space variables 61 | pub irez: f64, 62 | pub d2201: f64, 63 | pub d2211: f64, 64 | pub d3210: f64, 65 | pub d3222: f64, 66 | pub d4410: f64, 67 | pub d4422: f64, 68 | pub d5220: f64, 69 | pub d5232: f64, 70 | pub d5421: f64, 71 | pub d5433: f64, 72 | pub dedt: f64, 73 | pub del1: f64, 74 | pub del2: f64, 75 | pub del3: f64, 76 | pub didt: f64, 77 | pub dmdt: f64, 78 | pub dnodt: f64, 79 | pub domdt: f64, 80 | pub e3: f64, 81 | pub ee2: f64, 82 | pub peo: f64, 83 | pub pgho: f64, 84 | pub pho: f64, 85 | pub pinco: f64, 86 | pub plo: f64, 87 | pub se2: f64, 88 | pub se3: f64, 89 | pub sgh2: f64, 90 | pub sgh3: f64, 91 | pub sgh4: f64, 92 | pub sh2: f64, 93 | pub sh3: f64, 94 | pub si2: f64, 95 | pub si3: f64, 96 | pub sl2: f64, 97 | pub sl3: f64, 98 | pub sl4: f64, 99 | pub gsto: f64, 100 | pub xfact: f64, 101 | pub xgh2: f64, 102 | pub xgh3: f64, 103 | pub xgh4: f64, 104 | pub xh2: f64, 105 | pub xh3: f64, 106 | pub xi2: f64, 107 | pub xi3: f64, 108 | pub xl2: f64, 109 | pub xl3: f64, 110 | pub xl4: f64, 111 | pub xlamo: f64, 112 | pub zmol: f64, 113 | pub zmos: f64, 114 | pub atime: f64, 115 | pub xli: f64, 116 | pub xni: f64, 117 | } 118 | 119 | impl Satrec { 120 | pub fn zero() -> Satrec { 121 | Satrec { 122 | name: None, 123 | init: DpperInit::Y, 124 | operationmode: DpperOpsMode::I, 125 | error: 0, 126 | satnum: String::new(), 127 | epochyr: 0, 128 | epochdays: 0.0, 129 | ndot: 0.0, 130 | nddot: 0.0, 131 | bstar: 0.0, 132 | inclo: 0.0, 133 | nodeo: 0.0, 134 | ecco: 0.0, 135 | mo: 0.0, 136 | no: 0.0, 137 | a: 0.0, 138 | argpo: 0.0, 139 | alta: 0.0, 140 | altp: 0.0, 141 | jdsatepoch: 0.0, 142 | 143 | // Near Earth Variables 144 | isimp: 0, 145 | method: InitlMethod::N, 146 | aycof: 0.0, 147 | con41: 0.0, 148 | cc1: 0.0, 149 | cc4: 0.0, 150 | cc5: 0.0, 151 | d2: 0.0, 152 | d3: 0.0, 153 | d4: 0.0, 154 | delmo: 0.0, 155 | eta: 0.0, 156 | argpdot: 0.0, 157 | omgcof: 0.0, 158 | sinmao: 0.0, 159 | t: 0.0, 160 | t2cof: 0.0, 161 | t3cof: 0.0, 162 | t4cof: 0.0, 163 | t5cof: 0.0, 164 | x1mth2: 0.0, 165 | x7thm1: 0.0, 166 | mdot: 0.0, 167 | nodedot: 0.0, 168 | xlcof: 0.0, 169 | xmcof: 0.0, 170 | nodecf: 0.0, 171 | 172 | // Deep space variables 173 | irez: 0.0, 174 | d2201: 0.0, 175 | d2211: 0.0, 176 | d3210: 0.0, 177 | d3222: 0.0, 178 | d4410: 0.0, 179 | d4422: 0.0, 180 | d5220: 0.0, 181 | d5232: 0.0, 182 | d5421: 0.0, 183 | d5433: 0.0, 184 | dedt: 0.0, 185 | del1: 0.0, 186 | del2: 0.0, 187 | del3: 0.0, 188 | didt: 0.0, 189 | dmdt: 0.0, 190 | dnodt: 0.0, 191 | domdt: 0.0, 192 | e3: 0.0, 193 | ee2: 0.0, 194 | peo: 0.0, 195 | pgho: 0.0, 196 | pho: 0.0, 197 | pinco: 0.0, 198 | plo: 0.0, 199 | se2: 0.0, 200 | se3: 0.0, 201 | sgh2: 0.0, 202 | sgh3: 0.0, 203 | sgh4: 0.0, 204 | sh2: 0.0, 205 | sh3: 0.0, 206 | si2: 0.0, 207 | si3: 0.0, 208 | sl2: 0.0, 209 | sl3: 0.0, 210 | sl4: 0.0, 211 | gsto: 0.0, 212 | xfact: 0.0, 213 | xgh2: 0.0, 214 | xgh3: 0.0, 215 | xgh4: 0.0, 216 | xh2: 0.0, 217 | xh3: 0.0, 218 | xi2: 0.0, 219 | xi3: 0.0, 220 | xl2: 0.0, 221 | xl3: 0.0, 222 | xl4: 0.0, 223 | xlamo: 0.0, 224 | zmol: 0.0, 225 | zmos: 0.0, 226 | atime: 0.0, 227 | xli: 0.0, 228 | xni: 0.0, 229 | } 230 | } 231 | } 232 | 233 | pub fn parse_multiple(string: &str) -> (Vec, Vec) { 234 | let lines = string.split("\n").into_iter().filter(|el| { return el.trim() != "" }).collect::>(); 235 | let mut recs : Vec = vec![]; 236 | let mut errors: Vec = vec![]; 237 | let mut i = 0; 238 | while i < lines.len() { 239 | // If line 1 is not equal to a line operator, try next. 240 | if lines[i].trim().len() != 25 && i + 2 < lines.len() { 241 | match twoline2satrec(lines[i+1], lines[i+2]) { 242 | Ok(mut rec) => { 243 | if (lines[i].bytes().collect::>()[0] == '0' as u8) { 244 | rec.name = Some(lines[i][2..].trim().to_string()); 245 | } else { 246 | rec.name = Some(lines[i].trim().to_string()); 247 | } 248 | 249 | recs.push(rec); 250 | }, 251 | Err(err) => { 252 | errors.push(SatrecParseError::SatrecMultiError(i, Box::new(err))) 253 | } 254 | } 255 | i += 3; 256 | } else if i + 1 < lines.len() { 257 | match twoline2satrec(lines[i], lines[i+1]) { 258 | Ok(rec) => recs.push(rec), 259 | Err(err) => { 260 | errors.push(SatrecParseError::SatrecMultiError(i, Box::new(err))) 261 | } 262 | } 263 | i += 2; 264 | } else { 265 | errors.push(SatrecParseError::SatrecMultiError(i, Box::new(SatrecParseError::InvalidTLEBadLineCount))); 266 | i += 1; 267 | } 268 | } 269 | 270 | (recs, errors) 271 | } 272 | 273 | pub fn parse(string: &str) -> Result { 274 | let lines = string.split("\n").into_iter().filter(|el| { return el.trim() != "" }).collect::>(); 275 | 276 | // If there are three lines, parse as 3LE. 277 | if lines.len() == 3 { 278 | // First, perform check on the first character of each line. should be line numbers. 279 | if lines[1].bytes().collect::>()[0] != '1' as u8 || lines[2].bytes().collect::>()[0] != '2' as u8 { 280 | return Err(SatrecParseError::InvalidTLELineCheckFailed) 281 | } 282 | 283 | // Now, parse the satrec. 284 | let satrec = twoline2satrec(lines[1], lines[2]); 285 | 286 | // Because this is a 3le, we have a name. Extract/save it. 287 | match satrec { 288 | Ok(mut satrec) => { 289 | if (lines[0].bytes().collect::>()[0] == '0' as u8) { 290 | satrec.name = Some(lines[0][2..].to_string()); 291 | } else { 292 | satrec.name = Some(lines[0].to_string()); 293 | } 294 | 295 | return Ok(satrec) 296 | }, 297 | Err(err) => { return Err(err) } 298 | } 299 | } else if lines.len() == 2 { 300 | // First, perform check on the first character of each line. should be line numbers. 301 | if lines[0].bytes().collect::>()[0] != '1' as u8 || lines[1].bytes().collect::>()[0] != '2' as u8 { 302 | return Err(SatrecParseError::InvalidTLELineCheckFailed) 303 | } 304 | 305 | return twoline2satrec(lines[0], lines[1]); 306 | } else { 307 | return Err(SatrecParseError::InvalidTLEBadLineCount) 308 | } 309 | } 310 | 311 | pub fn twoline2satrec(str1: &str, str2: &str) -> Result { 312 | let mut satrec = parse_satrec(str1, str2)?; 313 | 314 | let opsmode = crate::propogation::dpper::DpperOpsMode::I; 315 | let opts = crate::propogation::sgp4init::SGP4InitOptions { 316 | opsmode: opsmode, 317 | satn: satrec.satnum.clone(), 318 | epoch: satrec.jdsatepoch - 2433281.5, 319 | xbstar: satrec.bstar, 320 | xecco: satrec.ecco, 321 | xargpo: satrec.argpo, 322 | xinclo: satrec.inclo, 323 | xmo: satrec.mo, 324 | xno: satrec.no, 325 | xnodeo: satrec.nodeo, 326 | }; 327 | 328 | match sgp4init::sgp4init(&mut satrec, opts) { 329 | Ok(_) => {}, // Ignore initial sgp4 positioning. 330 | Err(err) => return Err(SatrecParseError::Sgp4InitError(err)) 331 | }; 332 | 333 | return Ok(satrec); 334 | } 335 | 336 | #[derive(Debug, PartialEq)] 337 | pub enum SatrecParseError { 338 | FloatParseError(&'static str, usize, usize, String), 339 | IntParseError(&'static str, usize, usize, String), 340 | CompoundError(&'static str, String), 341 | Sgp4InitError(SGP4Error), 342 | InvalidTLELineCheckFailed, 343 | InvalidTLEBadLineCount, 344 | SatrecMultiError(usize, Box) 345 | } 346 | 347 | 348 | fn parse_float(line: &str, name: &'static str, low: usize, high: usize) -> Result { 349 | match line[low..high].trim().parse::() { 350 | Ok(res) => Ok(res), 351 | Err(num) => return Err(SatrecParseError::IntParseError(name, low, high, line[low..high].to_string())) 352 | } 353 | } 354 | 355 | fn parse_int(line: &str, name: &'static str, low: usize, high: usize) -> Result { 356 | match line[low..high].trim().parse::() { 357 | Ok(res) => Ok(res), 358 | Err(num) => return Err(SatrecParseError::IntParseError(name, low, high, line[low..high].to_string())) 359 | } 360 | } 361 | 362 | pub fn parse_satrec(str1: &str, str2: &str) -> Result { 363 | // Parse sat num 364 | let satnum = str1[2..7].trim(); 365 | 366 | // Parse epoch 367 | let epochyr = parse_int(str1, "epochyr", 18, 20)?; 368 | let epochdays = parse_float(str1, "epochdays", 20, 32)?; 369 | 370 | // Parse ndot 371 | let ndot = parse_float(str1, "ndot", 33, 43)? / (XPDOTP * 1440.0);; 372 | 373 | // Parse nndot 374 | let nddot_0 = parse_int(str1, "nndot frac", 44, 50)?; 375 | let nddot_1 = str1[50..52].to_string(); 376 | let nndot_str = format!(".{}E{}", nddot_0, nddot_1); 377 | 378 | let nddot = match nndot_str.parse::() { 379 | Ok(res) => res / (XPDOTP * 1440.0 * 1440.0), 380 | Err(_) => return Err(SatrecParseError::CompoundError("nndot", nndot_str)) 381 | }; 382 | 383 | // Parse bstar 384 | let bstar_0 = str1[53..54].to_string(); 385 | let bstar_1 = parse_int(str1, "bstar frac", 54, 59)?; 386 | let bstar_2 = str1[59..61].to_string(); 387 | let bstar_str = format!("{}.{}E{}", bstar_0, bstar_1, bstar_2); 388 | 389 | let bstar = match bstar_str.trim().parse::() { 390 | Ok(res) => res, 391 | Err(_) => return Err(SatrecParseError::CompoundError("bstr", bstar_str)) 392 | }; 393 | 394 | let inclo = parse_float(str2, "inclo", 8, 16)? * DEG_2_RAD; 395 | let nodeo = parse_float(str2, "nodeo", 17, 25)? * DEG_2_RAD; 396 | 397 | // Parse ecco 398 | let ecco_str = format!(".{}", str2[26..33].trim().to_string()); 399 | let ecco = match ecco_str.trim().parse::() { 400 | Ok(res) => res, 401 | Err(_) => return Err(SatrecParseError::CompoundError("ecco", ecco_str)) 402 | }; 403 | 404 | let argpo = parse_float(str2, "argpo", 34, 42)? * DEG_2_RAD; 405 | let mo = parse_float(str2, "mo", 43, 51)? * DEG_2_RAD; 406 | let no = parse_float(str2, "no", 52, 63)? / XPDOTP; 407 | 408 | let a = (no * TUMIN).powf(-2.0 / 3.0); 409 | 410 | let alta = (a * (1.0 + ecco)) - 1.0; 411 | let altp = (a * (1.0 - ecco)) - 1.0; 412 | 413 | let year; 414 | if epochyr < 57 { 415 | year = epochyr + 2000; 416 | } else { 417 | year = epochyr + 1900; 418 | } 419 | 420 | let jd = ext::days2mdhms(year as u64, epochdays); 421 | 422 | let jdsatepoch = ext::jday( 423 | year as f64, 424 | jd.month, 425 | jd.day, 426 | jd.hour, 427 | jd.minute, 428 | jd.second, 429 | 0.0, 430 | ); 431 | 432 | let mut satrec = Satrec::zero(); 433 | 434 | satrec.operationmode = DpperOpsMode::I; 435 | satrec.satnum = satnum.to_string(); 436 | satrec.epochyr = epochyr; 437 | satrec.epochdays = epochdays; 438 | satrec.ndot = ndot; 439 | satrec.nddot = nddot; 440 | satrec.bstar = bstar; 441 | satrec.inclo = inclo; 442 | satrec.nodeo = nodeo; 443 | satrec.ecco = ecco; 444 | satrec.argpo = argpo; 445 | satrec.mo = mo; 446 | satrec.no = no; 447 | satrec.a = a; 448 | satrec.alta = alta; 449 | satrec.altp = altp; 450 | satrec.jdsatepoch = jdsatepoch; 451 | 452 | return Ok(satrec); 453 | } 454 | 455 | #[cfg(test)] 456 | mod tests { 457 | #[test] 458 | fn test_parse_full() { 459 | let element = r###" 460 | 0 ISS (ZARYA) 461 | 1 25544U 98067A 19085.83761025 .00001292 00000-0 28282-4 0 9995 462 | 2 25544 51.6446 50.5941 0002332 117.0184 328.1109 15.52438493162461 463 | "###; 464 | let sat = crate::io::parse(element).unwrap(); 465 | assert_eq!(sat.name, Some("ISS (ZARYA)".to_string())); 466 | } 467 | 468 | #[test] 469 | fn test_parse_part() { 470 | let element = r###"1 25544U 98067A 19085.83761025 .00001292 00000-0 28282-4 0 9995 471 | 2 25544 51.6446 50.5941 0002332 117.0184 328.1109 15.52438493162461"###; 472 | let sat = crate::io::parse(element).unwrap(); 473 | assert_eq!(sat.name, None); 474 | } 475 | 476 | #[test] 477 | fn test_parse() { 478 | let satrec = crate::io::parse_satrec( 479 | "1 88888U 80275.98708465 .00073094 13844-3 66816-4 0 8", 480 | "2 88888 72.8435 115.9689 0086731 52.6988 110.5714 16.05824518 105", 481 | ).unwrap(); 482 | 483 | assert_eq!(satrec.error, 0); 484 | assert_eq!(satrec.satnum, "88888"); 485 | assert_eq!(satrec.epochyr, 80); 486 | assert_eq!(satrec.epochdays, 275.98708465); 487 | assert_eq!(satrec.ndot, 2.2148107004387767e-9); 488 | assert_eq!(satrec.nddot, 2.913090538750181e-13); 489 | assert_eq!(satrec.bstar, 0.000066816); 490 | assert_eq!(satrec.inclo, 1.2713589136764896); 491 | assert_eq!(satrec.nodeo, 2.0240391349160523); 492 | assert_eq!(satrec.ecco, 0.0086731); 493 | assert_eq!(satrec.argpo, 0.9197675718499877); 494 | assert_eq!(satrec.mo, 1.929834988539658); 495 | assert_eq!(satrec.no, 0.07006731262087737); 496 | assert_eq!(satrec.a, 1.0405013051291292); 497 | assert_eq!(satrec.alta, 0.04952567699864474); 498 | assert_eq!(satrec.altp, 0.03147693325961365); 499 | assert_eq!(satrec.jdsatepoch, 2444514.48708465); 500 | } 501 | 502 | 503 | // #[test] 504 | // fn test_parse_multi() { 505 | // let satrec = &crate::io::parse_multiple( 506 | // r###"1 88888U 80275.98708465 .00073094 13844-3 66816-4 0 8 507 | // 2 88888 72.8435 115.9689 0086731 52.6988 110.5714 16.05824518 105"### 508 | // ).unwrap()[0]; 509 | 510 | // assert_eq!(satrec.error, 0); 511 | // assert_eq!(satrec.satnum, "88888"); 512 | // assert_eq!(satrec.epochyr, 80); 513 | // assert_eq!(satrec.epochdays, 275.98708465); 514 | // assert_eq!(satrec.ndot, 2.2148107004387767e-9); 515 | // assert_eq!(satrec.nddot, 2.913090538750181e-13); 516 | // assert_eq!(satrec.bstar, 0.000066816); 517 | // assert_eq!(satrec.inclo, 1.2713589136764896); 518 | // assert_eq!(satrec.nodeo, 2.0240391349160523); 519 | // assert_eq!(satrec.ecco, 0.0086731); 520 | // assert_eq!(satrec.argpo, 0.9197675718499877); 521 | // assert_eq!(satrec.mo, 1.929834988539658); 522 | // assert_eq!(satrec.no, 0.07006731262087737); 523 | // assert_eq!(satrec.a, 1.0405013051291292); 524 | // assert_eq!(satrec.alta, 0.04952567699864474); 525 | // assert_eq!(satrec.altp, 0.03147693325961365); 526 | // assert_eq!(satrec.jdsatepoch, 2444514.48708465); 527 | // } 528 | 529 | #[test] 530 | fn test_init_tle() { 531 | let satrec = crate::io::twoline2satrec( 532 | "1 88888U 80275.98708465 .00073094 13844-3 66816-4 0 8", 533 | "2 88888 72.8435 115.9689 0086731 52.6988 110.5714 16.05824518 105", 534 | ).unwrap(); 535 | 536 | assert_eq!(satrec.error, 0); 537 | assert_eq!(satrec.satnum, "88888"); 538 | assert_eq!(satrec.epochyr, 80); 539 | assert_eq!(satrec.epochdays, 275.98708465); 540 | assert_eq!(satrec.ndot, 2.2148107004387767e-9); 541 | assert_eq!(satrec.nddot, 2.913090538750181e-13); 542 | assert_eq!(satrec.bstar, 0.000066816); 543 | assert_eq!(satrec.inclo, 1.2713589136764896); 544 | assert_eq!(satrec.nodeo, 2.0240391349160523); 545 | assert_eq!(satrec.ecco, 0.0086731); 546 | assert_eq!(satrec.argpo, 0.9197675718499877); 547 | assert_eq!(satrec.mo, 1.929834988539658); 548 | assert_eq!(satrec.no, 0.07010615621239219); 549 | assert_eq!(satrec.a, 1.0405013051291292); 550 | assert_eq!(satrec.alta, 0.04952567699864474); 551 | assert_eq!(satrec.altp, 0.03147693325961365); 552 | assert_eq!(satrec.jdsatepoch, 2444514.48708465); 553 | assert_eq!(satrec.isimp, 1); 554 | assert_eq!(satrec.method, crate::propogation::initl::InitlMethod::N); 555 | assert_eq!(satrec.aycof, 0.001117407997657797); 556 | 557 | assert_eq!(satrec.con41, -0.7389556198424165); 558 | assert_eq!(satrec.cc1, 2.3340379369349495e-8); 559 | assert_eq!(satrec.cc4, 0.00037724513079719584); 560 | assert_eq!(satrec.cc5, 0.01233625966048993); 561 | assert_eq!(satrec.d2, 0.0); 562 | assert_eq!(satrec.d3, 0.0); 563 | assert_eq!(satrec.d4, 0.0); 564 | assert_eq!(satrec.delmo, 0.6963031736886937); 565 | assert_eq!(satrec.eta, 0.32347784078169217); 566 | assert_eq!(satrec.argpdot, -0.000029718644394179532); 567 | assert!((satrec.omgcof - 1.6306928260750368e-7).abs() < 1e-15); 568 | assert_eq!(satrec.sinmao, 0.9362350458581234); 569 | assert_eq!(satrec.t, 0.0); 570 | assert_eq!(satrec.t2cof, 3.5010569054024244e-8); 571 | assert_eq!(satrec.t3cof, 0.0); 572 | assert_eq!(satrec.t4cof, 0.0); 573 | assert_eq!(satrec.t5cof, 0.0); 574 | assert_eq!(satrec.x1mth2, 0.9129852066141388); 575 | assert_eq!(satrec.x7thm1, -0.3908964462989719); 576 | assert_eq!(satrec.mdot, 0.07006729343154267); 577 | assert_eq!(satrec.nodedot, -0.00003096533062994484); 578 | assert_eq!(satrec.xlcof, 0.0019306451483792333); 579 | assert_eq!(satrec.xmcof, -0.0000493564796620572); 580 | assert_eq!(satrec.nodecf, -2.5361112971222384e-12); 581 | assert_eq!(satrec.irez, 0.0); 582 | assert_eq!(satrec.d2201, 0.0); 583 | assert_eq!(satrec.d2211, 0.0); 584 | assert_eq!(satrec.d3210, 0.0); 585 | assert_eq!(satrec.d3222, 0.0); 586 | assert_eq!(satrec.d4410, 0.0); 587 | assert_eq!(satrec.d4422, 0.0); 588 | assert_eq!(satrec.d5220, 0.0); 589 | assert_eq!(satrec.d5232, 0.0); 590 | assert_eq!(satrec.d5421, 0.0); 591 | assert_eq!(satrec.d5433, 0.0); 592 | assert_eq!(satrec.dedt, 0.0); 593 | assert_eq!(satrec.del1, 0.0); 594 | assert_eq!(satrec.del2, 0.0); 595 | assert_eq!(satrec.del3, 0.0); 596 | assert_eq!(satrec.didt, 0.0); 597 | assert_eq!(satrec.dmdt, 0.0); 598 | assert_eq!(satrec.dnodt, 0.0); 599 | assert_eq!(satrec.domdt, 0.0); 600 | assert_eq!(satrec.e3, 0.0); 601 | assert_eq!(satrec.ee2, 0.0); 602 | assert_eq!(satrec.peo, 0.0); 603 | assert_eq!(satrec.pgho, 0.0); 604 | assert_eq!(satrec.pho, 0.0); 605 | assert_eq!(satrec.pinco, 0.0); 606 | assert_eq!(satrec.plo, 0.0); 607 | assert_eq!(satrec.se2, 0.0); 608 | assert_eq!(satrec.se3, 0.0); 609 | assert_eq!(satrec.sgh2, 0.0); 610 | assert_eq!(satrec.sgh3, 0.0); 611 | assert_eq!(satrec.sgh4, 0.0); 612 | assert_eq!(satrec.sh2, 0.0); 613 | assert_eq!(satrec.sh3, 0.0); 614 | assert_eq!(satrec.si2, 0.0); 615 | assert_eq!(satrec.si3, 0.0); 616 | assert_eq!(satrec.sl2, 0.0); 617 | assert_eq!(satrec.sl3, 0.0); 618 | assert_eq!(satrec.sl4, 0.0); 619 | assert_eq!(satrec.gsto, 0.1082901416688955); 620 | assert_eq!(satrec.xfact, 0.0); 621 | assert_eq!(satrec.xgh2, 0.0); 622 | assert_eq!(satrec.xgh3, 0.0); 623 | assert_eq!(satrec.xgh4, 0.0); 624 | assert_eq!(satrec.xh2, 0.0); 625 | assert_eq!(satrec.xh3, 0.0); 626 | assert_eq!(satrec.xi2, 0.0); 627 | assert_eq!(satrec.xi3, 0.0); 628 | assert_eq!(satrec.xl2, 0.0); 629 | assert_eq!(satrec.xl3, 0.0); 630 | assert_eq!(satrec.xl4, 0.0); 631 | assert_eq!(satrec.xlamo, 0.0); 632 | assert_eq!(satrec.zmol, 0.0); 633 | assert_eq!(satrec.zmos, 0.0); 634 | assert_eq!(satrec.atime, 0.0); 635 | assert_eq!(satrec.xli, 0.0); 636 | assert_eq!(satrec.xni, 0.0); 637 | } 638 | } 639 | -------------------------------------------------------------------------------- /src/propogation/sgp4init.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::*; 2 | 3 | /*----------------------------------------------------------------------------- 4 | * 5 | * procedure sgp4init 6 | * 7 | * this procedure initializes variables for sgp4. 8 | * 9 | * author : david vallado 719-573-2600 28 jun 2005 10 | * author : david vallado 719-573-2600 28 jun 2005 11 | * 12 | * inputs : 13 | * opsmode - mode of operation afspc or improved 'a', 'i' 14 | * satn - satellite number 15 | * bstar - sgp4 type drag coefficient kg/m2er 16 | * ecco - eccentricity 17 | * epoch - epoch time in days from jan 0, 1950. 0 hr 18 | * argpo - argument of perigee (output if ds) 19 | * inclo - inclination 20 | * mo - mean anomaly (output if ds) 21 | * no - mean motion 22 | * nodeo - right ascension of ascending node 23 | * 24 | * outputs : 25 | * rec - common values for subsequent calls 26 | * return code - non-zero on error. 27 | * 1 - mean elements, ecc >= 1.0 or ecc < -0.001 or a < 0.95 er 28 | * 2 - mean motion less than 0.0 29 | * 3 - pert elements, ecc < 0.0 or ecc > 1.0 30 | * 4 - semi-latus rectum < 0.0 31 | * 5 - epoch elements are sub-orbital 32 | * 6 - satellite has decayed 33 | * 34 | * locals : 35 | * cnodm , snodm , cosim , sinim , cosomm , sinomm 36 | * cc1sq , cc2 , cc3 37 | * coef , coef1 38 | * cosio4 - 39 | * day - 40 | * dndt - 41 | * em - eccentricity 42 | * emsq - eccentricity squared 43 | * eeta - 44 | * etasq - 45 | * gam - 46 | * argpm - argument of perigee 47 | * nodem - 48 | * inclm - inclination 49 | * mm - mean anomaly 50 | * nm - mean motion 51 | * perige - perigee 52 | * pinvsq - 53 | * psisq - 54 | * qzms24 - 55 | * rtemsq - 56 | * s1, s2, s3, s4, s5, s6, s7 - 57 | * sfour - 58 | * ss1, ss2, ss3, ss4, ss5, ss6, ss7 - 59 | * sz1, sz2, sz3 60 | * sz11, sz12, sz13, sz21, sz22, sz23, sz31, sz32, sz33 - 61 | * tc - 62 | * temp - 63 | * temp1, temp2, temp3 - 64 | * tsi - 65 | * xpidot - 66 | * xhdot1 - 67 | * z1, z2, z3 - 68 | * z11, z12, z13, z21, z22, z23, z31, z32, z33 - 69 | * 70 | * coupling : 71 | * getgravconst- 72 | * initl - 73 | * dscom - 74 | * dpper - 75 | * dsinit - 76 | * sgp4 - 77 | * 78 | * references : 79 | * hoots, roehrich, norad spacetrack report #3 1980 80 | * hoots, norad spacetrack report #6 1986 81 | * hoots, schumacher and glover 2004 82 | * vallado, crawford, hujsak, kelso 2006 83 | ----------------------------------------------------------------------------*/ 84 | 85 | use crate::io::Satrec; 86 | use crate::propogation::dpper::*; 87 | use crate::propogation::dscom::*; 88 | use crate::propogation::dsinit::*; 89 | use crate::propogation::initl::*; 90 | use crate::propogation::sgp4::*; 91 | 92 | pub struct SGP4InitOptions { 93 | pub opsmode: DpperOpsMode, 94 | pub satn: String, 95 | pub epoch: f64, 96 | pub xbstar: f64, 97 | pub xecco: f64, 98 | pub xargpo: f64, 99 | pub xinclo: f64, 100 | pub xmo: f64, 101 | pub xno: f64, 102 | pub xnodeo: f64, 103 | } 104 | pub fn sgp4init(satrec: &mut Satrec, options: SGP4InitOptions) -> Result { 105 | let SGP4InitOptions { 106 | opsmode, 107 | satn, 108 | epoch, 109 | xbstar, 110 | xecco, 111 | xargpo, 112 | xinclo, 113 | xmo, 114 | xno, 115 | xnodeo, 116 | } = options; 117 | 118 | let mut _cosim: f64; 119 | let mut _sinim: f64; 120 | let cc1sq; 121 | let cc2; 122 | let mut cc3; 123 | let coef; 124 | let coef1; 125 | let cosio4; 126 | let mut _em: f64; 127 | let mut _emsq: f64; 128 | let eeta; 129 | let etasq; 130 | let argpm; 131 | let nodem; 132 | let inclm; 133 | let mm; 134 | let mut _nm: f64; 135 | let perige; 136 | let pinvsq; 137 | let psisq; 138 | let mut qzms24; 139 | let mut _s1: f64; 140 | let mut _s2: f64; 141 | let mut _s3: f64; 142 | let mut _s4: f64; 143 | let mut _s5: f64; 144 | let mut sfour; 145 | let mut _ss1: f64; 146 | let mut _ss2: f64; 147 | let mut _ss3: f64; 148 | let mut _ss4: f64; 149 | let mut _ss5: f64; 150 | let mut _sz1: f64; 151 | let mut _sz3: f64; 152 | let mut _sz11: f64; 153 | let mut _sz13: f64; 154 | let mut _sz21: f64; 155 | let mut _sz23: f64; 156 | let mut _sz31: f64; 157 | let mut _sz33: f64; 158 | let tc; 159 | let temp; 160 | let temp1; 161 | let temp2; 162 | let temp3; 163 | let tsi; 164 | let xpidot; 165 | let xhdot1; 166 | let mut _z1: f64; 167 | let mut _z3: f64; 168 | let mut _z11: f64; 169 | let mut _z13: f64; 170 | let mut _z21: f64; 171 | let mut _z23: f64; 172 | let mut _z31: f64; 173 | let mut _z33: f64; 174 | 175 | /* ------------------------ initialization --------------------- */ 176 | // sgp4fix divisor for divide by zero check on inclination 177 | // the old check used 1.0 + Math.cos(pi-1.0e-9), but then compared it to 178 | // 1.5 e-12, so the threshold was changed to 1.5e-12 for consistency 179 | const TEMP4: f64 = 1.5e-12; 180 | 181 | // ----------- set all near earth variables to zero ------------ 182 | satrec.isimp = 0; 183 | satrec.method = crate::propogation::initl::InitlMethod::N; 184 | satrec.aycof = 0.0; 185 | satrec.con41 = 0.0; 186 | satrec.cc1 = 0.0; 187 | satrec.cc4 = 0.0; 188 | satrec.cc5 = 0.0; 189 | satrec.d2 = 0.0; 190 | satrec.d3 = 0.0; 191 | satrec.d4 = 0.0; 192 | satrec.delmo = 0.0; 193 | satrec.eta = 0.0; 194 | satrec.argpdot = 0.0; 195 | satrec.omgcof = 0.0; 196 | satrec.sinmao = 0.0; 197 | satrec.t = 0.0; 198 | satrec.t2cof = 0.0; 199 | satrec.t3cof = 0.0; 200 | satrec.t4cof = 0.0; 201 | satrec.t5cof = 0.0; 202 | satrec.x1mth2 = 0.0; 203 | satrec.x7thm1 = 0.0; 204 | satrec.mdot = 0.0; 205 | satrec.nodedot = 0.0; 206 | satrec.xlcof = 0.0; 207 | satrec.xmcof = 0.0; 208 | satrec.nodecf = 0.0; 209 | 210 | // ----------- set all deep space variables to zero ------------ 211 | satrec.irez = 0.0; 212 | satrec.d2201 = 0.0; 213 | satrec.d2211 = 0.0; 214 | satrec.d3210 = 0.0; 215 | satrec.d3222 = 0.0; 216 | satrec.d4410 = 0.0; 217 | satrec.d4422 = 0.0; 218 | satrec.d5220 = 0.0; 219 | satrec.d5232 = 0.0; 220 | satrec.d5421 = 0.0; 221 | satrec.d5433 = 0.0; 222 | satrec.dedt = 0.0; 223 | satrec.del1 = 0.0; 224 | satrec.del2 = 0.0; 225 | satrec.del3 = 0.0; 226 | satrec.didt = 0.0; 227 | satrec.dmdt = 0.0; 228 | satrec.dnodt = 0.0; 229 | satrec.domdt = 0.0; 230 | satrec.e3 = 0.0; 231 | satrec.ee2 = 0.0; 232 | satrec.peo = 0.0; 233 | satrec.pgho = 0.0; 234 | satrec.pho = 0.0; 235 | satrec.pinco = 0.0; 236 | satrec.plo = 0.0; 237 | satrec.se2 = 0.0; 238 | satrec.se3 = 0.0; 239 | satrec.sgh2 = 0.0; 240 | satrec.sgh3 = 0.0; 241 | satrec.sgh4 = 0.0; 242 | satrec.sh2 = 0.0; 243 | satrec.sh3 = 0.0; 244 | satrec.si2 = 0.0; 245 | satrec.si3 = 0.0; 246 | satrec.sl2 = 0.0; 247 | satrec.sl3 = 0.0; 248 | satrec.sl4 = 0.0; 249 | satrec.gsto = 0.0; 250 | satrec.xfact = 0.0; 251 | satrec.xgh2 = 0.0; 252 | satrec.xgh3 = 0.0; 253 | satrec.xgh4 = 0.0; 254 | satrec.xh2 = 0.0; 255 | satrec.xh3 = 0.0; 256 | satrec.xi2 = 0.0; 257 | satrec.xi3 = 0.0; 258 | satrec.xl2 = 0.0; 259 | satrec.xl3 = 0.0; 260 | satrec.xl4 = 0.0; 261 | satrec.xlamo = 0.0; 262 | satrec.zmol = 0.0; 263 | satrec.zmos = 0.0; 264 | satrec.atime = 0.0; 265 | satrec.xli = 0.0; 266 | satrec.xni = 0.0; 267 | 268 | // sgp4fix - note the following variables are also passed directly via satrec. 269 | // it is possible to streamline the sgp4init call by deleting the "x" 270 | // variables, but the user would need to set the satrec.* values first. we 271 | // include the additional assignments in case twoline2rv is not used. 272 | 273 | satrec.bstar = xbstar; 274 | satrec.ecco = xecco; 275 | satrec.argpo = xargpo; 276 | satrec.inclo = xinclo; 277 | satrec.mo = xmo; 278 | satrec.no = xno; 279 | satrec.nodeo = xnodeo; 280 | 281 | // sgp4fix add opsmode 282 | satrec.operationmode = opsmode; 283 | 284 | // ------------------------ earth constants ----------------------- 285 | // sgp4fix identify constants and allow alternate values 286 | 287 | let ss = (78.0 / EARTH_RADIUS) + 1.0; 288 | // sgp4fix use multiply for speed instead of pow 289 | let qzms2ttemp = (120.0 - 78.0) / EARTH_RADIUS; 290 | let qzms2t = qzms2ttemp * qzms2ttemp * qzms2ttemp * qzms2ttemp; 291 | 292 | satrec.init = DpperInit::Y; 293 | satrec.t = 0.0; 294 | 295 | let initl_options = InitlOptions { 296 | satn, 297 | ecco: satrec.ecco, 298 | 299 | epoch, 300 | inclo: satrec.inclo, 301 | no: satrec.no, 302 | 303 | method: satrec.method.clone(), 304 | opsmode: satrec.operationmode.clone(), 305 | }; 306 | 307 | let initl_result = initl(initl_options); 308 | 309 | let InitlReturn { 310 | ao, 311 | con42, 312 | cosio, 313 | cosio2, 314 | eccsq, 315 | omeosq, 316 | posq, 317 | rp, 318 | rteosq, 319 | sinio, 320 | .. 321 | } = initl_result; 322 | 323 | satrec.no = initl_result.no; 324 | satrec.con41 = initl_result.con41; 325 | satrec.gsto = initl_result.gsto; 326 | satrec.error = 0; 327 | 328 | // sgp4fix remove this check as it is unnecessary 329 | // the mrt check in sgp4 handles decaying satellite cases even if the starting 330 | // condition is below the surface of te earth 331 | // if (rp < 1.0) 332 | // { 333 | // printf("// *** satn%d epoch elts sub-orbital ***\n", satn); 334 | // satrec.error = 5; 335 | // } 336 | 337 | if omeosq >= 0.0 || satrec.no >= 0.0 { 338 | satrec.isimp = 0; 339 | // TODO: logic mode error. 340 | if rp < (220.0 / EARTH_RADIUS) + 1.0 { 341 | satrec.isimp = 1; 342 | } 343 | sfour = ss; 344 | qzms24 = qzms2t; 345 | perige = (rp - 1.0) * EARTH_RADIUS; 346 | 347 | // - for perigees below 156 km, s and qoms2t are altered - 348 | if perige < 156.0 { 349 | sfour = perige - 78.0; 350 | if perige < 98.0 { 351 | sfour = 20.0; 352 | } 353 | 354 | // sgp4fix use multiply for speed instead of pow 355 | let qzms24temp = (120.0 - sfour) / EARTH_RADIUS; 356 | qzms24 = qzms24temp * qzms24temp * qzms24temp * qzms24temp; 357 | sfour = (sfour / EARTH_RADIUS) + 1.0; 358 | } 359 | pinvsq = 1.0 / posq; 360 | 361 | tsi = 1.0 / (ao - sfour); 362 | satrec.eta = ao * satrec.ecco * tsi; 363 | etasq = satrec.eta * satrec.eta; 364 | eeta = satrec.ecco * satrec.eta; 365 | psisq = (1.0 - etasq).abs(); 366 | coef = qzms24 * (tsi.powi(4)); 367 | coef1 = coef / (psisq.powf(3.5)); 368 | cc2 = coef1 369 | * satrec.no 370 | * ((ao * (1.0 + (1.5 * etasq) + (eeta * (4.0 + etasq)))) 371 | + (((0.375 * J2 * tsi) / psisq) 372 | * satrec.con41 373 | * (8.0 + (3.0 * etasq * (8.0 + etasq))))); 374 | satrec.cc1 = satrec.bstar * cc2; 375 | cc3 = 0.0; 376 | if satrec.ecco > 1.0e-4 { 377 | cc3 = (-2.0 * coef * tsi * J3OJ2 * satrec.no * sinio) / satrec.ecco; 378 | } 379 | satrec.x1mth2 = 1.0 - cosio2; 380 | satrec.cc4 = 2.0 381 | * satrec.no 382 | * coef1 383 | * ao 384 | * omeosq 385 | * (((satrec.eta * (2.0 + (0.5 * etasq))) + (satrec.ecco * (0.5 + (2.0 * etasq)))) 386 | - (((J2 * tsi) / (ao * psisq)) 387 | * ((-3.0 388 | * satrec.con41 389 | * ((1.0 - (2.0 * eeta)) + (etasq * (1.5 - (0.5 * eeta))))) 390 | + (0.75 391 | * satrec.x1mth2 392 | * ((2.0 * etasq) - (eeta * (1.0 + etasq))) 393 | * (2.0 * satrec.argpo).cos())))); 394 | satrec.cc5 = 2.0 * coef1 * ao * omeosq * (1.0 + (2.75 * (etasq + eeta)) + (eeta * etasq)); 395 | cosio4 = cosio2 * cosio2; 396 | temp1 = 1.5 * J2 * pinvsq * satrec.no; 397 | temp2 = 0.5 * temp1 * J2 * pinvsq; 398 | temp3 = -0.46875 * J4 * pinvsq * pinvsq * satrec.no; 399 | satrec.mdot = satrec.no 400 | + (0.5 * temp1 * rteosq * satrec.con41) 401 | + (0.0625 * temp2 * rteosq * ((13.0 - (78.0 * cosio2)) + (137.0 * cosio4))); 402 | satrec.argpdot = (-0.5 * temp1 * con42) 403 | + (0.0625 * temp2 * ((7.0 - (114.0 * cosio2)) + (395.0 * cosio4))) 404 | + (temp3 * ((3.0 - (36.0 * cosio2)) + (49.0 * cosio4))); 405 | xhdot1 = -temp1 * cosio; 406 | satrec.nodedot = xhdot1 407 | + (((0.5 * temp2 * (4.0 - (19.0 * cosio2))) + (2.0 * temp3 * (3.0 - (7.0 * cosio2)))) 408 | * cosio); 409 | xpidot = satrec.argpdot + satrec.nodedot; 410 | satrec.omgcof = satrec.bstar * cc3 * (satrec.argpo).cos(); 411 | satrec.xmcof = 0.0; 412 | if satrec.ecco > 1.0e-4 { 413 | satrec.xmcof = (-X2O3 * coef * satrec.bstar) / eeta; 414 | } 415 | satrec.nodecf = 3.5 * omeosq * xhdot1 * satrec.cc1; 416 | satrec.t2cof = 1.5 * satrec.cc1; 417 | 418 | // sgp4fix for divide by zero with xinco = 180 deg 419 | if (cosio + 1.0).abs() > 1.5e-12 { 420 | satrec.xlcof = (-0.25 * J3OJ2 * sinio * (3.0 + (5.0 * cosio))) / (1.0 + cosio); 421 | } else { 422 | satrec.xlcof = (-0.25 * J3OJ2 * sinio * (3.0 + (5.0 * cosio))) / TEMP4; 423 | } 424 | satrec.aycof = -0.5 * J3OJ2 * sinio; 425 | 426 | // sgp4fix use multiply for speed instead of pow 427 | let delmotemp = 1.0 + (satrec.eta * (satrec.mo).cos()); 428 | satrec.delmo = delmotemp * delmotemp * delmotemp; 429 | satrec.sinmao = satrec.mo.sin(); 430 | satrec.x7thm1 = (7.0 * cosio2) - 1.0; 431 | 432 | // --------------- deep space initialization ------------- 433 | if TWO_PI / satrec.no >= 225.0 { 434 | satrec.method = InitlMethod::D; 435 | satrec.isimp = 1; 436 | tc = 0.0; 437 | inclm = satrec.inclo; 438 | 439 | let dscom_options = DscomOptions { 440 | epoch, 441 | ep: satrec.ecco, 442 | argpp: satrec.argpo, 443 | tc, 444 | inclp: satrec.inclo, 445 | nodep: satrec.nodeo, 446 | 447 | np: satrec.no, 448 | // e3: satrec.e3, 449 | // ee2: satrec.ee2, 450 | 451 | // peo: satrec.peo, 452 | // pgho: satrec.pgho, 453 | // pho: satrec.pho, 454 | // pinco: satrec.pinco, 455 | 456 | // plo: satrec.plo, 457 | // se2: satrec.se2, 458 | // se3: satrec.se3, 459 | 460 | // sgh2: satrec.sgh2, 461 | // sgh3: satrec.sgh3, 462 | // sgh4: satrec.sgh4, 463 | 464 | // sh2: satrec.sh2, 465 | // sh3: satrec.sh3, 466 | // si2: satrec.si2, 467 | // si3: satrec.si3, 468 | 469 | // sl2: satrec.sl2, 470 | // sl3: satrec.sl3, 471 | // sl4: satrec.sl4, 472 | 473 | // xgh2: satrec.xgh2, 474 | // xgh3: satrec.xgh3, 475 | // xgh4: satrec.xgh4, 476 | // xh2: satrec.xh2, 477 | 478 | // xh3: satrec.xh3, 479 | // xi2: satrec.xi2, 480 | // xi3: satrec.xi3, 481 | // xl2: satrec.xl2, 482 | 483 | // xl3: satrec.xl3, 484 | // xl4: satrec.xl4, 485 | 486 | // zmol: satrec.zmol, 487 | // zmos: satrec.zmos, 488 | }; 489 | 490 | let dscom_result = dscom(dscom_options); 491 | 492 | satrec.e3 = dscom_result.e3; 493 | satrec.ee2 = dscom_result.ee2; 494 | 495 | satrec.peo = dscom_result.peo; 496 | satrec.pgho = dscom_result.pgho; 497 | satrec.pho = dscom_result.pho; 498 | 499 | satrec.pinco = dscom_result.pinco; 500 | satrec.plo = dscom_result.plo; 501 | satrec.se2 = dscom_result.se2; 502 | satrec.se3 = dscom_result.se3; 503 | 504 | satrec.sgh2 = dscom_result.sgh2; 505 | satrec.sgh3 = dscom_result.sgh3; 506 | satrec.sgh4 = dscom_result.sgh4; 507 | satrec.sh2 = dscom_result.sh2; 508 | satrec.sh3 = dscom_result.sh3; 509 | 510 | satrec.si2 = dscom_result.si2; 511 | satrec.si3 = dscom_result.si3; 512 | satrec.sl2 = dscom_result.sl2; 513 | satrec.sl3 = dscom_result.sl3; 514 | satrec.sl4 = dscom_result.sl4; 515 | 516 | let DscomResult { 517 | sinim, 518 | cosim, 519 | em, 520 | emsq, 521 | s1, 522 | s2, 523 | s3, 524 | s4, 525 | s5, 526 | ss1, 527 | ss2, 528 | ss3, 529 | ss4, 530 | ss5, 531 | sz1, 532 | sz3, 533 | sz11, 534 | sz13, 535 | sz21, 536 | sz23, 537 | sz31, 538 | sz33, 539 | .. 540 | } = dscom_result; 541 | 542 | satrec.xgh2 = dscom_result.xgh2; 543 | satrec.xgh3 = dscom_result.xgh3; 544 | satrec.xgh4 = dscom_result.xgh4; 545 | satrec.xh2 = dscom_result.xh2; 546 | satrec.xh3 = dscom_result.xh3; 547 | satrec.xi2 = dscom_result.xi2; 548 | satrec.xi3 = dscom_result.xi3; 549 | satrec.xl2 = dscom_result.xl2; 550 | satrec.xl3 = dscom_result.xl3; 551 | satrec.xl4 = dscom_result.xl4; 552 | satrec.zmol = dscom_result.zmol; 553 | satrec.zmos = dscom_result.zmos; 554 | 555 | let DscomResult { 556 | nm, 557 | z1, 558 | z3, 559 | z11, 560 | z13, 561 | z21, 562 | z23, 563 | z31, 564 | z33, 565 | .. 566 | } = dscom_result; 567 | 568 | let dpper_options = DpperOptions { 569 | inclo: inclm, 570 | init: satrec.init.clone(), 571 | ep: satrec.ecco, 572 | inclp: satrec.inclo, 573 | nodep: satrec.nodeo, 574 | argpp: satrec.argpo, 575 | mp: satrec.mo, 576 | opsmode: satrec.operationmode.clone(), 577 | }; 578 | 579 | let dpper_result = dpper(&satrec, dpper_options); 580 | 581 | satrec.ecco = dpper_result.ep; 582 | satrec.inclo = dpper_result.inclp; 583 | satrec.nodeo = dpper_result.nodep; 584 | satrec.argpo = dpper_result.argpp; 585 | satrec.mo = dpper_result.mp; 586 | 587 | argpm = 0.0; 588 | nodem = 0.0; 589 | mm = 0.0; 590 | 591 | let dsinit_options = DsinitOptions { 592 | cosim, 593 | emsq, 594 | argpo: satrec.argpo, 595 | s1, 596 | s2, 597 | s3, 598 | s4, 599 | s5, 600 | sinim, 601 | ss1, 602 | ss2, 603 | ss3, 604 | ss4, 605 | ss5, 606 | sz1, 607 | sz3, 608 | sz11, 609 | sz13, 610 | sz21, 611 | sz23, 612 | sz31, 613 | sz33, 614 | t: satrec.t, 615 | tc, 616 | gsto: satrec.gsto, 617 | mo: satrec.mo, 618 | mdot: satrec.mdot, 619 | no: satrec.no, 620 | nodeo: satrec.nodeo, 621 | nodedot: satrec.nodedot, 622 | xpidot, 623 | z1, 624 | z3, 625 | z11, 626 | z13, 627 | z21, 628 | z23, 629 | z31, 630 | z33, 631 | ecco: satrec.ecco, 632 | eccsq, 633 | em, 634 | argpm, 635 | inclm, 636 | mm, 637 | nm, 638 | nodem, 639 | irez: satrec.irez, 640 | atime: satrec.atime, 641 | d2201: satrec.d2201, 642 | d2211: satrec.d2211, 643 | d3210: satrec.d3210, 644 | d3222: satrec.d3222, 645 | d4410: satrec.d4410, 646 | d4422: satrec.d4422, 647 | d5220: satrec.d5220, 648 | d5232: satrec.d5232, 649 | d5421: satrec.d5421, 650 | d5433: satrec.d5433, 651 | dedt: satrec.dedt, 652 | didt: satrec.didt, 653 | dmdt: satrec.dmdt, 654 | dnodt: satrec.dnodt, 655 | domdt: satrec.domdt, 656 | del1: satrec.del1, 657 | del2: satrec.del2, 658 | del3: satrec.del3, 659 | xfact: satrec.xfact, 660 | xlamo: satrec.xlamo, 661 | xli: satrec.xli, 662 | xni: satrec.xni, 663 | }; 664 | 665 | let dsinit_result = dsinit(dsinit_options); 666 | 667 | satrec.irez = dsinit_result.irez; 668 | satrec.atime = dsinit_result.atime; 669 | satrec.d2201 = dsinit_result.d2201; 670 | satrec.d2211 = dsinit_result.d2211; 671 | 672 | satrec.d3210 = dsinit_result.d3210; 673 | satrec.d3222 = dsinit_result.d3222; 674 | satrec.d4410 = dsinit_result.d4410; 675 | satrec.d4422 = dsinit_result.d4422; 676 | satrec.d5220 = dsinit_result.d5220; 677 | 678 | satrec.d5232 = dsinit_result.d5232; 679 | satrec.d5421 = dsinit_result.d5421; 680 | satrec.d5433 = dsinit_result.d5433; 681 | satrec.dedt = dsinit_result.dedt; 682 | satrec.didt = dsinit_result.didt; 683 | 684 | satrec.dmdt = dsinit_result.dmdt; 685 | satrec.dnodt = dsinit_result.dnodt; 686 | satrec.domdt = dsinit_result.domdt; 687 | satrec.del1 = dsinit_result.del1; 688 | 689 | satrec.del2 = dsinit_result.del2; 690 | satrec.del3 = dsinit_result.del3; 691 | satrec.xfact = dsinit_result.xfact; 692 | satrec.xlamo = dsinit_result.xlamo; 693 | satrec.xli = dsinit_result.xli; 694 | 695 | satrec.xni = dsinit_result.xni; 696 | } 697 | 698 | // ----------- set variables if not deep space ----------- 699 | if satrec.isimp != 1 { 700 | cc1sq = satrec.cc1 * satrec.cc1; 701 | satrec.d2 = 4.0 * ao * tsi * cc1sq; 702 | temp = (satrec.d2 * tsi * satrec.cc1) / 3.0; 703 | satrec.d3 = ((17.0 * ao) + sfour) * temp; 704 | satrec.d4 = 0.5 * temp * ao * tsi * ((221.0 * ao) + (31.0 * sfour)) * satrec.cc1; 705 | satrec.t3cof = satrec.d2 + (2.0 * cc1sq); 706 | satrec.t4cof = 707 | 0.25 * ((3.0 * satrec.d3) + (satrec.cc1 * ((12.0 * satrec.d2) + (10.0 * cc1sq)))); 708 | satrec.t5cof = 0.2 709 | * ((3.0 * satrec.d4) 710 | + (12.0 * satrec.cc1 * satrec.d3) 711 | + (6.0 * satrec.d2 * satrec.d2) 712 | + (15.0 * cc1sq * ((2.0 * satrec.d2) + cc1sq))); 713 | } 714 | 715 | /* finally propogate to zero epoch to initialize all others. */ 716 | // sgp4fix take out check to let satellites process until they are actually below earth surface 717 | // if(satrec.error == 0) 718 | } 719 | 720 | let result = sgp4(satrec, 0.0)?; // TODO: found extra param:, 0); 721 | 722 | satrec.init = DpperInit::N; 723 | 724 | return Ok(result) 725 | } 726 | --------------------------------------------------------------------------------