gistools/space/propagation/
dpper.rs

1use crate::space::{OperationMode, Satellite};
2use core::f64::consts::{PI, TAU};
3use libm::{atan2, cos, fabs, sin};
4
5/// Options for Dpper
6#[derive(Debug, Default, Clone, Copy, PartialEq)]
7pub struct DpperOptions {
8    /// true to initialize
9    pub init: bool,
10    /// epoch
11    pub ep: f64,
12    /// mean anomaly
13    pub inclp: f64,
14    /// right ascension of ascending node
15    pub nodep: f64,
16    /// argument of perigee
17    pub argpp: f64,
18    /// mean anomaly
19    pub mp: f64,
20}
21
22/// Output for Dpper
23#[derive(Debug, Default, Clone, Copy, PartialEq)]
24pub struct DpperOutput {
25    /// true anomaly
26    pub ep: f64,
27    /// inclination
28    pub inclp: f64,
29    /// right ascension of ascending node
30    pub nodep: f64,
31    /// argument of perigee
32    pub argpp: f64,
33    /// mean anomaly
34    pub mp: f64,
35}
36
37/// procedure dpper
38///
39/// this procedure provides deep space long period periodic contributions
40/// to the mean elements.  by design, these periodics are zero at epoch.
41/// this used to be dscom which included initialization, but it's really a
42/// recurring function.
43///
44/// author         david vallado                  719-573-2600   28 jun 2005
45///
46/// references
47/// - hoots, roehrich, norad spacetrack report #3 1980
48/// - hoots, norad spacetrack report #6 1986
49/// - hoots, schumacher and glover 2004
50/// - vallado, crawford, hujsak, kelso  2006
51///
52/// ## Parameters
53/// - `sat`: Satellite object
54/// - `options`: dpper options
55/// - `tsince`: time in minutes since epoch
56///
57/// ## Returns
58/// Deep space long period periodic contributions
59pub fn dpper(
60    sat: &Satellite,
61    options: DpperOptions,
62    tsince: f64, // defaults to 0 (sgp4init doesn't set a time)
63) -> DpperOutput {
64    let Satellite {
65        e3,
66        ee2,
67        peo,
68        pgho,
69        pho,
70        pinco,
71        plo,
72        se2,
73        se3,
74        sgh2,
75        sgh3,
76        sgh4,
77        sh2,
78        sh3,
79        si2,
80        si3,
81        sl2,
82        sl3,
83        sl4,
84        xgh2,
85        xgh3,
86        xgh4,
87        xh2,
88        xh3,
89        xi2,
90        xi3,
91        xl2,
92        xl3,
93        xl4,
94        zmol,
95        zmos,
96        opsmode,
97        ..
98    } = sat;
99
100    let DpperOptions { init, mut ep, mut inclp, mut nodep, mut argpp, mut mp } = options;
101
102    // Copy satellite attributes into local variables for convenience
103    // and symmetry in writing formulae.
104
105    let mut alfdp: f64;
106    let mut betdp: f64;
107    let cosip: f64;
108    let sinip: f64;
109    let cosop: f64;
110    let sinop: f64;
111    let dalf: f64;
112    let dbet: f64;
113    let dls: f64;
114    let mut f2: f64;
115    let mut f3: f64;
116    let mut pe: f64;
117    let mut pgh: f64;
118    let mut ph: f64;
119    let mut pinc: f64;
120    let mut pl: f64;
121    let mut sinzf: f64;
122    let mut xls: f64;
123    let xnoh: f64;
124    let mut zf: f64;
125
126    //  ---------------------- constants -----------------------------
127    let zns = 1.19459e-5;
128    let zes = 0.01675;
129    let znl = 1.5835218e-4;
130    let zel = 0.0549;
131
132    //  --------------- calculate time varying periodics -----------
133    let mut zm = zmos + zns * tsince;
134
135    // be sure that the initial call has time set to zero
136    if init {
137        zm = *zmos;
138    }
139    zf = zm + 2.0 * zes * sin(zm);
140    sinzf = sin(zf);
141    f2 = 0.5 * sinzf * sinzf - 0.25;
142    f3 = -0.5 * sinzf * cos(zf);
143
144    let ses = se2 * f2 + se3 * f3;
145    let sis = si2 * f2 + si3 * f3;
146    let sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf;
147    let sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf;
148    let shs = sh2 * f2 + sh3 * f3;
149
150    zm = zmol + znl * tsince;
151    if init {
152        zm = *zmol;
153    }
154
155    zf = zm + 2.0 * zel * sin(zm);
156    sinzf = sin(zf);
157    f2 = 0.5 * sinzf * sinzf - 0.25;
158    f3 = -0.5 * sinzf * cos(zf);
159
160    let sel = ee2 * f2 + e3 * f3;
161    let sil = xi2 * f2 + xi3 * f3;
162    let sll = xl2 * f2 + xl3 * f3 + xl4 * sinzf;
163    let sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf;
164    let shll = xh2 * f2 + xh3 * f3;
165
166    pe = ses + sel;
167    pinc = sis + sil;
168    pl = sls + sll;
169    pgh = sghs + sghl;
170    ph = shs + shll;
171
172    if !init {
173        pe -= peo;
174        pinc -= pinco;
175        pl -= plo;
176        pgh -= pgho;
177        ph -= pho;
178        inclp += pinc;
179        ep += pe;
180        sinip = sin(inclp);
181        cosip = cos(inclp);
182
183        /* ----------------- apply periodics directly ------------ */
184        // sgp4fix for lyddane choice
185        // strn3 used original inclination - this is technically feasible
186        // gsfc used perturbed inclination - also technically feasible
187        // probably best to readjust the 0.2 limit value and limit discontinuity
188        // 0.2 rad = 11.45916 deg
189        // use next line for original strn3 approach and original inclination
190        // if (inclo >= 0.2)
191        // use next line for gsfc version and perturbed inclination
192        if inclp >= 0.2 {
193            ph /= sinip;
194            pgh -= cosip * ph;
195            argpp += pgh;
196            nodep += ph;
197            mp += pl;
198        } else {
199            //  ---- apply periodics with lyddane modification ----
200            sinop = sin(nodep);
201            cosop = cos(nodep);
202            alfdp = sinip * sinop;
203            betdp = sinip * cosop;
204            dalf = ph * cosop + pinc * cosip * sinop;
205            dbet = -ph * sinop + pinc * cosip * cosop;
206            alfdp += dalf;
207            betdp += dbet;
208            nodep %= TAU;
209
210            //  sgp4fix for afspc written intrinsic functions
211            //  nodep used without a trigonometric function ahead
212            if nodep < 0.0 && *opsmode == OperationMode::A {
213                nodep += TAU;
214            }
215            xls = mp + argpp + cosip * nodep;
216            dls = pl + pgh - pinc * nodep * sinip;
217            xls += dls;
218            xnoh = nodep;
219            nodep = atan2(alfdp, betdp);
220
221            //  sgp4fix for afspc written intrinsic functions
222            //  nodep used without a trigonometric function ahead
223            if nodep < 0.0 && *opsmode == OperationMode::A {
224                nodep += TAU;
225            }
226            if fabs(xnoh - nodep) > PI {
227                if nodep < xnoh {
228                    nodep += TAU;
229                } else {
230                    nodep -= TAU;
231                }
232            }
233            mp += pl;
234            argpp = xls - mp - cosip * nodep;
235        }
236    }
237
238    DpperOutput { ep, inclp, nodep, argpp, mp }
239}