1 | /* this file contains routines to support Earth satellites.
|
---|
2 | *
|
---|
3 | * Orbit propagation is based on the NORAD SGP4/SDP4 code, as converted from
|
---|
4 | * the original FORTRAN to C by Magnus Backstrom. The paper "Spacetrack
|
---|
5 | * Report Number 3: Models for Propagation of NORAD Element Sets" describes
|
---|
6 | * the calculations.
|
---|
7 | * See http://www.celestrak.com/NORAD/documentation/spacetrk.pdf.
|
---|
8 | *
|
---|
9 | * A few topocentric routines are also used from the 'orbit' program which is
|
---|
10 | * Copyright (c) 1986,1987,1988,1989,1990 Robert W. Berger N3EMO
|
---|
11 | *
|
---|
12 | */
|
---|
13 |
|
---|
14 | /* define this to use orbit's propagator
|
---|
15 | #define USE_ORBIT_PROPAGATOR
|
---|
16 | */
|
---|
17 |
|
---|
18 | /* define this to print some stuff
|
---|
19 | #define ESAT_TRACE
|
---|
20 | */
|
---|
21 |
|
---|
22 | #include <stdio.h>
|
---|
23 | #include <math.h>
|
---|
24 | #include <string.h>
|
---|
25 | #include <stdlib.h>
|
---|
26 |
|
---|
27 | #include "astro.h"
|
---|
28 | #include "preferences.h"
|
---|
29 |
|
---|
30 | #include "vector.h"
|
---|
31 | #include "sattypes.h"
|
---|
32 | #include "satlib.h"
|
---|
33 |
|
---|
34 |
|
---|
35 | #define ESAT_MAG 2 /* fake satellite magnitude */
|
---|
36 |
|
---|
37 | typedef double MAT3x3[3][3];
|
---|
38 |
|
---|
39 | static int crazyOp (Now *np, Obj *op);
|
---|
40 | static void esat_prop (Now *np, Obj *op, double *SatX, double *SatY, double
|
---|
41 | *SatZ, double *SatVX, double *SatVY, double *SatVZ);
|
---|
42 | static void GetSatelliteParams (Obj *op);
|
---|
43 | static void GetSiteParams (Now *np);
|
---|
44 | static double Kepler (double MeanAnomaly, double Eccentricity);
|
---|
45 | static void GetSubSatPoint (double SatX, double SatY, double SatZ,
|
---|
46 | double T, double *Latitude, double *Longitude, double *Height);
|
---|
47 | static void GetSatPosition (double EpochTime, double EpochRAAN,
|
---|
48 | double EpochArgPerigee, double SemiMajorAxis, double Inclination,
|
---|
49 | double Eccentricity, double RAANPrecession, double PerigeePrecession,
|
---|
50 | double T, double TrueAnomaly, double *X, double *Y, double *Z,
|
---|
51 | double *Radius, double *VX, double *VY, double *VZ);
|
---|
52 | static void GetSitPosition (double SiteLat, double SiteLong,
|
---|
53 | double SiteElevation, double CrntTime, double *SiteX, double *SiteY,
|
---|
54 | double *SiteZ, double *SiteVX, double *SiteVY, MAT3x3 SiteMatrix);
|
---|
55 | static void GetRange (double SiteX, double SiteY, double SiteZ,
|
---|
56 | double SiteVX, double SiteVY, double SatX, double SatY, double SatZ,
|
---|
57 | double SatVX, double SatVY, double SatVZ, double *Range,
|
---|
58 | double *RangeRate);
|
---|
59 | static void GetTopocentric (double SatX, double SatY, double SatZ,
|
---|
60 | double SiteX, double SiteY, double SiteZ, MAT3x3 SiteMatrix, double *X,
|
---|
61 | double *Y, double *Z);
|
---|
62 | static void GetBearings (double SatX, double SatY, double SatZ,
|
---|
63 | double SiteX, double SiteY, double SiteZ, MAT3x3 SiteMatrix,
|
---|
64 | double *Azimuth, double *Elevation);
|
---|
65 | static int Eclipsed (double SatX, double SatY, double SatZ,
|
---|
66 | double SatRadius, double CrntTime);
|
---|
67 | static void InitOrbitRoutines (double EpochDay, int AtEod);
|
---|
68 |
|
---|
69 | #ifdef USE_ORBIT_PROPAGATOR
|
---|
70 | static void GetPrecession (double SemiMajorAxis, double Eccentricity,
|
---|
71 | double Inclination, double *RAANPrecession, double *PerigeePrecession);
|
---|
72 | #endif /* USE_ORBIT_PROPAGATOR */
|
---|
73 |
|
---|
74 | /* stuff from orbit */
|
---|
75 | /* char VersionStr[] = "N3EMO Orbit Simulator v3.9"; */
|
---|
76 |
|
---|
77 | #ifdef PI2
|
---|
78 | #undef PI2
|
---|
79 | #endif
|
---|
80 |
|
---|
81 | #define PI2 (PI*2)
|
---|
82 |
|
---|
83 | #define MinutesPerDay (24*60.0)
|
---|
84 | #define SecondsPerDay (60*MinutesPerDay)
|
---|
85 | #define HalfSecond (0.5/SecondsPerDay)
|
---|
86 | #define EarthRadius 6378.16 /* Kilometers */
|
---|
87 | #define C 2.997925e5 /* Kilometers/Second */
|
---|
88 | #define RadiansPerDegree (PI/180)
|
---|
89 | #define ABS(x) ((x) < 0 ? (-(x)) : (x))
|
---|
90 | #define SQR(x) ((x)*(x))
|
---|
91 |
|
---|
92 | #define EarthFlat (1/298.25) /* Earth Flattening Coeff. */
|
---|
93 | #define SiderealSolar 1.0027379093
|
---|
94 | #define SidRate (PI2*SiderealSolar/SecondsPerDay) /* radians/second */
|
---|
95 | #define GM 398600 /* Kilometers^3/seconds^2 */
|
---|
96 |
|
---|
97 | #define Epsilon (RadiansPerDegree/3600) /* 1 arc second */
|
---|
98 | #define SunRadius 695000
|
---|
99 | #define SunSemiMajorAxis 149598845.0 /* Kilometers */
|
---|
100 |
|
---|
101 | /* Keplerian Elements and misc. data for the satellite */
|
---|
102 | static double EpochDay; /* time of epoch */
|
---|
103 | static double EpochMeanAnomaly; /* Mean Anomaly at epoch */
|
---|
104 | static long EpochOrbitNum; /* Integer orbit # of epoch */
|
---|
105 | static double EpochRAAN; /* RAAN at epoch */
|
---|
106 | static double epochMeanMotion; /* Revolutions/day */
|
---|
107 | static double OrbitalDecay; /* Revolutions/day^2 */
|
---|
108 | static double EpochArgPerigee; /* argument of perigee at epoch */
|
---|
109 | static double Eccentricity;
|
---|
110 | static double Inclination;
|
---|
111 |
|
---|
112 | /* Site Parameters */
|
---|
113 | static double SiteLat,SiteLong,SiteAltitude;
|
---|
114 |
|
---|
115 |
|
---|
116 | static double SidDay,SidReference; /* Date and sidereal time */
|
---|
117 |
|
---|
118 | /* Keplerian elements for the sun */
|
---|
119 | static double SunEpochTime,SunInclination,SunRAAN,SunEccentricity,
|
---|
120 | SunArgPerigee,SunMeanAnomaly,SunMeanMotion;
|
---|
121 |
|
---|
122 | /* values for shadow geometry */
|
---|
123 | static double SinPenumbra,CosPenumbra;
|
---|
124 |
|
---|
125 |
|
---|
126 | /* given a Now and an Obj with info about an earth satellite in the es_* fields
|
---|
127 | * fill in the s_* sky fields describing the satellite.
|
---|
128 | * as usual, we compute the geocentric ra/dec precessed to np->n_epoch and
|
---|
129 | * compute topocentric altitude accounting for refraction.
|
---|
130 | * return 0 if all ok, else -1.
|
---|
131 | */
|
---|
132 | int
|
---|
133 | obj_earthsat (Now *np, Obj *op)
|
---|
134 | {
|
---|
135 | double Radius; /* From geocenter */
|
---|
136 | double SatX,SatY,SatZ; /* In Right Ascension based system */
|
---|
137 | double SatVX,SatVY,SatVZ; /* Kilometers/second */
|
---|
138 | double SiteX,SiteY,SiteZ;
|
---|
139 | double SiteVX,SiteVY;
|
---|
140 | double SiteMatrix[3][3];
|
---|
141 | double Height;
|
---|
142 | double SSPLat,SSPLong;
|
---|
143 | double Azimuth,Elevation,Range;
|
---|
144 | double RangeRate;
|
---|
145 | double dtmp;
|
---|
146 | double CrntTime;
|
---|
147 | double ra, dec;
|
---|
148 |
|
---|
149 | #ifdef ESAT_TRACE
|
---|
150 | printf ("\n");
|
---|
151 | printf ("Name = %s\n", op->o_name);
|
---|
152 | printf ("current jd = %13.5f\n", mjd+MJD0);
|
---|
153 | printf ("current mjd = %g\n", mjd);
|
---|
154 | printf ("satellite jd = %13.5f\n", op->es_epoch+MJD0);
|
---|
155 | printf ("satellite mjd = %g\n", op->es_epoch);
|
---|
156 | #endif /* ESAT_TRACE */
|
---|
157 |
|
---|
158 | /* xephem uses noon 12/31/1899 as 0; orbit uses midnight 1/1/1900.
|
---|
159 | * thus, xephem runs 12 hours, or 1/2 day, behind of what orbit wants.
|
---|
160 | */
|
---|
161 | CrntTime = mjd + 0.5;
|
---|
162 |
|
---|
163 | /* extract the XEphem data forms into those used by orbit.
|
---|
164 | * (we still use some functions and names from orbit, thank you).
|
---|
165 | */
|
---|
166 | InitOrbitRoutines(CrntTime, 1);
|
---|
167 | GetSatelliteParams(op);
|
---|
168 | GetSiteParams(np);
|
---|
169 |
|
---|
170 | /* propagate to np->n_mjd */
|
---|
171 | esat_prop (np, op, &SatX, &SatY, &SatZ, &SatVX, &SatVY, &SatVZ);
|
---|
172 | Radius = sqrt (SatX*SatX + SatY*SatY + SatZ*SatZ);
|
---|
173 |
|
---|
174 | /* find geocentric EOD equatorial directly from xyz vector */
|
---|
175 | dtmp = atan2 (SatY, SatX);
|
---|
176 | range (&dtmp, 2*PI);
|
---|
177 | op->s_gaera = (float) dtmp;
|
---|
178 | op->s_gaedec = (float) atan2 (SatZ, sqrt(SatX*SatX + SatY*SatY));
|
---|
179 |
|
---|
180 | /* find topocentric from site location */
|
---|
181 | GetSitPosition(SiteLat,SiteLong,SiteAltitude,CrntTime,
|
---|
182 | &SiteX,&SiteY,&SiteZ,&SiteVX,&SiteVY,SiteMatrix);
|
---|
183 | GetBearings(SatX,SatY,SatZ,SiteX,SiteY,SiteZ,SiteMatrix,
|
---|
184 | &Azimuth,&Elevation);
|
---|
185 |
|
---|
186 | op->s_az = Azimuth;
|
---|
187 | refract (pressure, temp, Elevation, &dtmp);
|
---|
188 | op->s_alt = dtmp;
|
---|
189 |
|
---|
190 | /* Range: line-of-site distance to satellite, m
|
---|
191 | * RangeRate: m/s
|
---|
192 | */
|
---|
193 | GetRange(SiteX,SiteY,SiteZ,SiteVX,SiteVY,
|
---|
194 | SatX,SatY,SatZ,SatVX,SatVY,SatVZ,&Range,&RangeRate);
|
---|
195 |
|
---|
196 | op->s_range = (float)(Range*1000); /* we want m */
|
---|
197 | op->s_rangev = (float)(RangeRate*1000); /* we want m/s */
|
---|
198 |
|
---|
199 | /* SSPLat: sub-satellite latitude, rads
|
---|
200 | * SSPLong: sub-satellite longitude, >0 west, rads
|
---|
201 | * Height: height of satellite above ground, m
|
---|
202 | */
|
---|
203 | GetSubSatPoint(SatX,SatY,SatZ,CrntTime,
|
---|
204 | &SSPLat,&SSPLong,&Height);
|
---|
205 |
|
---|
206 | op->s_elev = (float)(Height*1000); /* we want m */
|
---|
207 | op->s_sublat = (float)SSPLat;
|
---|
208 | op->s_sublng = (float)(-SSPLong); /* we want +E */
|
---|
209 |
|
---|
210 | op->s_eclipsed = Eclipsed(SatX,SatY,SatZ,Radius,CrntTime);
|
---|
211 |
|
---|
212 | #ifdef ESAT_TRACE
|
---|
213 | printf ("CrntTime = %g\n", CrntTime);
|
---|
214 | printf ("SatX = %g\n", SatX);
|
---|
215 | printf ("SatY = %g\n", SatY);
|
---|
216 | printf ("SatZ = %g\n", SatZ);
|
---|
217 | printf ("Radius = %g\n", Radius);
|
---|
218 | printf ("SatVX = %g\n", SatVX);
|
---|
219 | printf ("SatVY = %g\n", SatVY);
|
---|
220 | printf ("SatVZ = %g\n", SatVZ);
|
---|
221 | printf ("SiteX = %g\n", SiteX);
|
---|
222 | printf ("SiteY = %g\n", SiteY);
|
---|
223 | printf ("SiteZ = %g\n", SiteZ);
|
---|
224 | printf ("SiteVX = %g\n", SiteVX);
|
---|
225 | printf ("SiteVY = %g\n", SiteVY);
|
---|
226 | printf ("Height = %g\n", Height);
|
---|
227 | printf ("SSPLat = %g\n", SSPLat);
|
---|
228 | printf ("SSPLong = %g\n", SSPLong);
|
---|
229 | printf ("Azimuth = %g\n", Azimuth);
|
---|
230 | printf ("Elevation = %g\n", Elevation);
|
---|
231 | printf ("Range = %g\n", Range);
|
---|
232 | printf ("RangeRate = %g\n", RangeRate);
|
---|
233 | fflush (stdout);
|
---|
234 | #endif /* ESAT_TRACE */
|
---|
235 |
|
---|
236 | /* find s_ra/dec, depending on current options. */
|
---|
237 | if (pref_get(PREF_EQUATORIAL) == PREF_TOPO) {
|
---|
238 | double ha, lst;
|
---|
239 | aa_hadec (lat, Elevation, (double)op->s_az, &ha, &dec);
|
---|
240 | now_lst (np, &lst);
|
---|
241 | ra = hrrad(lst) - ha;
|
---|
242 | range (&ra, 2*PI);
|
---|
243 | } else {
|
---|
244 | ra = op->s_gaera;
|
---|
245 | dec = op->s_gaedec;
|
---|
246 | }
|
---|
247 | if (epoch != EOD)
|
---|
248 | precess (mjd, epoch, &ra, &dec);
|
---|
249 | op->s_ra = (float)ra;
|
---|
250 | op->s_dec = (float)dec;
|
---|
251 |
|
---|
252 | /* just make up a size and brightness */
|
---|
253 | set_smag (op, ESAT_MAG);
|
---|
254 | op->s_size = (float)0;
|
---|
255 |
|
---|
256 | return (0);
|
---|
257 | }
|
---|
258 |
|
---|
259 | /* find position and velocity vector for given Obj at the given time.
|
---|
260 | * set USE_ORBIT_PROPAGATOR depending on desired propagator to use.
|
---|
261 | */
|
---|
262 | static void
|
---|
263 | esat_prop (Now *np, Obj *op, double *SatX, double *SatY, double *SatZ,
|
---|
264 | double *SatVX, double *SatVY, double *SatVZ)
|
---|
265 | {
|
---|
266 | #ifdef USE_ORBIT_PROPAGATOR
|
---|
267 | double ReferenceOrbit; /* Floating point orbit # at epoch */
|
---|
268 | double CurrentOrbit;
|
---|
269 | long OrbitNum;
|
---|
270 | double RAANPrecession,PerigeePrecession;
|
---|
271 | double MeanAnomaly,TrueAnomaly;
|
---|
272 | double SemiMajorAxis;
|
---|
273 | double AverageMotion, /* Corrected for drag */
|
---|
274 | CurrentMotion;
|
---|
275 | double Radius;
|
---|
276 | double CrntTime;
|
---|
277 |
|
---|
278 | if (crazyOp (np, op)) {
|
---|
279 | *SatX = *SatY = *SatZ = *SatVX = *SatVY = *SatVZ = 0;
|
---|
280 | return;
|
---|
281 | }
|
---|
282 |
|
---|
283 | SemiMajorAxis = 331.25 * exp(2*log(MinutesPerDay/epochMeanMotion)/3);
|
---|
284 | GetPrecession(SemiMajorAxis,Eccentricity,Inclination,&RAANPrecession,
|
---|
285 | &PerigeePrecession);
|
---|
286 |
|
---|
287 | ReferenceOrbit = EpochMeanAnomaly/PI2 + EpochOrbitNum;
|
---|
288 |
|
---|
289 | CrntTime = mjd + 0.5;
|
---|
290 | AverageMotion = epochMeanMotion + (CrntTime-EpochDay)*OrbitalDecay/2;
|
---|
291 | CurrentMotion = epochMeanMotion + (CrntTime-EpochDay)*OrbitalDecay;
|
---|
292 |
|
---|
293 | SemiMajorAxis = 331.25 * exp(2*log(MinutesPerDay/CurrentMotion)/3);
|
---|
294 |
|
---|
295 | CurrentOrbit = ReferenceOrbit + (CrntTime-EpochDay)*AverageMotion;
|
---|
296 |
|
---|
297 | OrbitNum = CurrentOrbit;
|
---|
298 |
|
---|
299 | MeanAnomaly = (CurrentOrbit-OrbitNum)*PI2;
|
---|
300 |
|
---|
301 | TrueAnomaly = Kepler(MeanAnomaly,Eccentricity);
|
---|
302 |
|
---|
303 | GetSatPosition(EpochDay,EpochRAAN,EpochArgPerigee,SemiMajorAxis,
|
---|
304 | Inclination,Eccentricity,RAANPrecession,PerigeePrecession,
|
---|
305 | CrntTime,TrueAnomaly,SatX,SatY,SatZ,&Radius,SatVX,SatVY,SatVZ);
|
---|
306 |
|
---|
307 | #ifdef ESAT_TRACE
|
---|
308 | printf ("O Radius = %g\n", Radius);
|
---|
309 | printf ("ReferenceOrbit = %g\n", ReferenceOrbit);
|
---|
310 | printf ("CurrentOrbit = %g\n", CurrentOrbit);
|
---|
311 | printf ("RAANPrecession = %g\n", RAANPrecession);
|
---|
312 | printf ("PerigeePrecession = %g\n", PerigeePrecession);
|
---|
313 | printf ("MeanAnomaly = %g\n", MeanAnomaly);
|
---|
314 | printf ("TrueAnomaly = %g\n", TrueAnomaly);
|
---|
315 | printf ("SemiMajorAxis = %g\n", SemiMajorAxis);
|
---|
316 | printf ("AverageMotion = %g\n", AverageMotion);
|
---|
317 | printf ("CurrentMotion = %g\n", CurrentMotion);
|
---|
318 | #endif /* ESAT_TRACE */
|
---|
319 |
|
---|
320 | #else /* ! USE_ORBIT_PROPAGATOR */
|
---|
321 | #define MPD 1440.0 /* minutes per day */
|
---|
322 |
|
---|
323 | SatElem se;
|
---|
324 | SatData sd;
|
---|
325 | Vec3 posvec, velvec;
|
---|
326 | double dy;
|
---|
327 | double dt;
|
---|
328 | int yr;
|
---|
329 |
|
---|
330 | if (crazyOp (np, op)) {
|
---|
331 | *SatX = *SatY = *SatZ = *SatVX = *SatVY = *SatVZ = 0;
|
---|
332 | return;
|
---|
333 | }
|
---|
334 |
|
---|
335 | /* init */
|
---|
336 | memset ((void *)&se, 0, sizeof(se));
|
---|
337 | memset ((void *)&sd, 0, sizeof(sd));
|
---|
338 | sd.elem = &se;
|
---|
339 |
|
---|
340 | /* se_EPOCH is packed as yr*1000 + dy, where yr is years since 1900
|
---|
341 | * and dy is day of year, Jan 1 being 1
|
---|
342 | */
|
---|
343 | mjd_dayno (op->es_epoch, &yr, &dy);
|
---|
344 | yr -= 1900;
|
---|
345 | dy += 1;
|
---|
346 | se.se_EPOCH = yr*1000 + dy;
|
---|
347 |
|
---|
348 | /* others carry over with some change in units */
|
---|
349 | se.se_XNO = op->es_n * (2*PI/MPD); /* revs/day to rads/min */
|
---|
350 | se.se_XINCL = (float)degrad(op->es_inc);
|
---|
351 | se.se_XNODEO = (float)degrad(op->es_raan);
|
---|
352 | se.se_EO = op->es_e;
|
---|
353 | se.se_OMEGAO = (float)degrad(op->es_ap);
|
---|
354 | se.se_XMO = (float)degrad(op->es_M);
|
---|
355 | se.se_BSTAR = op->es_drag;
|
---|
356 | se.se_XNDT20 = op->es_decay*(2*PI/MPD/MPD); /*rv/dy^^2 to rad/min^^2*/
|
---|
357 |
|
---|
358 | se.se_id.orbit = op->es_orbit;
|
---|
359 |
|
---|
360 | dt = (mjd-op->es_epoch)*MPD;
|
---|
361 |
|
---|
362 | #ifdef ESAT_TRACE
|
---|
363 | printf ("se_EPOCH : %30.20f\n", se.se_EPOCH);
|
---|
364 | printf ("se_XNO : %30.20f\n", se.se_XNO);
|
---|
365 | printf ("se_XINCL : %30.20f\n", se.se_XINCL);
|
---|
366 | printf ("se_XNODEO : %30.20f\n", se.se_XNODEO);
|
---|
367 | printf ("se_EO : %30.20f\n", se.se_EO);
|
---|
368 | printf ("se_OMEGAO : %30.20f\n", se.se_OMEGAO);
|
---|
369 | printf ("se_XMO : %30.20f\n", se.se_XMO);
|
---|
370 | printf ("se_BSTAR : %30.20f\n", se.se_BSTAR);
|
---|
371 | printf ("se_XNDT20 : %30.20f\n", se.se_XNDT20);
|
---|
372 | printf ("se_orbit : %30d\n", se.se_id.orbit);
|
---|
373 | printf ("dt : %30.20f\n", dt);
|
---|
374 | #endif /* ESAT_TRACE */
|
---|
375 |
|
---|
376 | /* compute the state vectors */
|
---|
377 | if (se.se_XNO >= (1.0/225.0))
|
---|
378 | sgp4(&sd, &posvec, &velvec, dt); /* NEO */
|
---|
379 | else
|
---|
380 | sdp4(&sd, &posvec, &velvec, dt); /* GEO */
|
---|
381 | if (sd.prop.sgp4)
|
---|
382 | free (sd.prop.sgp4); /* sd.prop.sdp4 is in same union */
|
---|
383 | if (sd.deep)
|
---|
384 | free (sd.deep);
|
---|
385 |
|
---|
386 | *SatX = ERAD*posvec.x/1000; /* earth radii to km */
|
---|
387 | *SatY = ERAD*posvec.y/1000;
|
---|
388 | *SatZ = ERAD*posvec.z/1000;
|
---|
389 | *SatVX = 100*velvec.x; /* ?? */
|
---|
390 | *SatVY = 100*velvec.y;
|
---|
391 | *SatVZ = 100*velvec.z;
|
---|
392 | #endif
|
---|
393 | }
|
---|
394 |
|
---|
395 | /* return 1 if op is crazy @ np */
|
---|
396 | static int
|
---|
397 | crazyOp (Now *np, Obj *op)
|
---|
398 | {
|
---|
399 | /* toss if more than a year old */
|
---|
400 | return (fabs(op->es_epoch - mjd) > 365);
|
---|
401 | }
|
---|
402 |
|
---|
403 | /* grab the xephem stuff from op and copy into orbit's globals.
|
---|
404 | */
|
---|
405 | static void
|
---|
406 | GetSatelliteParams(Obj *op)
|
---|
407 | {
|
---|
408 | /* the following are for the orbit functions */
|
---|
409 | /* xephem uses noon 12/31/1899 as 0; orbit uses midnight 1/1/1900 as 1.
|
---|
410 | * thus, xephem runs 12 hours, or 1/2 day, behind of what orbit wants.
|
---|
411 | */
|
---|
412 | EpochDay = op->es_epoch + 0.5;
|
---|
413 |
|
---|
414 | /* xephem stores inc in degrees; orbit wants rads */
|
---|
415 | Inclination = degrad(op->es_inc);
|
---|
416 |
|
---|
417 | /* xephem stores RAAN in degrees; orbit wants rads */
|
---|
418 | EpochRAAN = degrad(op->es_raan);
|
---|
419 |
|
---|
420 | Eccentricity = op->es_e;
|
---|
421 |
|
---|
422 | /* xephem stores arg of perigee in degrees; orbit wants rads */
|
---|
423 | EpochArgPerigee = degrad(op->es_ap);
|
---|
424 |
|
---|
425 | /* xephem stores mean anomaly in degrees; orbit wants rads */
|
---|
426 | EpochMeanAnomaly = degrad (op->es_M);
|
---|
427 |
|
---|
428 | epochMeanMotion = op->es_n;
|
---|
429 |
|
---|
430 | OrbitalDecay = op->es_decay;
|
---|
431 |
|
---|
432 | EpochOrbitNum = op->es_orbit;
|
---|
433 | }
|
---|
434 |
|
---|
435 |
|
---|
436 |
|
---|
437 | static void
|
---|
438 | GetSiteParams(Now *np)
|
---|
439 | {
|
---|
440 | SiteLat = lat;
|
---|
441 |
|
---|
442 | /* xephem stores longitude as >0 east; orbit wants >0 west */
|
---|
443 | SiteLong = 2.0*PI - lng;
|
---|
444 |
|
---|
445 | /* what orbit calls altitude xephem calls elevation and stores it from
|
---|
446 | * sea level in earth radii; orbit wants km
|
---|
447 | */
|
---|
448 | SiteAltitude = elev*ERAD/1000.0;
|
---|
449 |
|
---|
450 | /* we don't implement a minimum horizon altitude cutoff
|
---|
451 | SiteMinElev = 0;
|
---|
452 | */
|
---|
453 |
|
---|
454 | #ifdef ESAT_TRACE
|
---|
455 | printf ("SiteLat = %g\n", SiteLat);
|
---|
456 | printf ("SiteLong = %g\n", SiteLong);
|
---|
457 | printf ("SiteAltitude = %g\n", SiteAltitude);
|
---|
458 | fflush (stdout);
|
---|
459 | #endif
|
---|
460 | }
|
---|
461 |
|
---|
462 |
|
---|
463 | /* Solve Kepler's equation */
|
---|
464 | /* Inputs: */
|
---|
465 | /* MeanAnomaly Time Since last perigee, in radians. */
|
---|
466 | /* PI2 = one complete orbit. */
|
---|
467 | /* Eccentricity Eccentricity of orbit's ellipse. */
|
---|
468 | /* Output: */
|
---|
469 | /* TrueAnomaly Angle between perigee, geocenter, and */
|
---|
470 | /* current position. */
|
---|
471 |
|
---|
472 | static
|
---|
473 | double Kepler(double MeanAnomaly, double Eccentricity)
|
---|
474 | {
|
---|
475 | register double E; /* Eccentric Anomaly */
|
---|
476 | register double Error;
|
---|
477 | register double TrueAnomaly;
|
---|
478 |
|
---|
479 | E = MeanAnomaly ;/*+ Eccentricity*sin(MeanAnomaly); -- Initial guess */
|
---|
480 | do
|
---|
481 | {
|
---|
482 | Error = (E - Eccentricity*sin(E) - MeanAnomaly)
|
---|
483 | / (1 - Eccentricity*cos(E));
|
---|
484 | E -= Error;
|
---|
485 | }
|
---|
486 | while (ABS(Error) >= Epsilon);
|
---|
487 |
|
---|
488 | if (ABS(E-PI) < Epsilon)
|
---|
489 | TrueAnomaly = PI;
|
---|
490 | else
|
---|
491 | TrueAnomaly = 2*atan(sqrt((1+Eccentricity)/(1-Eccentricity))
|
---|
492 | *tan(E/2));
|
---|
493 | if (TrueAnomaly < 0)
|
---|
494 | TrueAnomaly += PI2;
|
---|
495 |
|
---|
496 | return TrueAnomaly;
|
---|
497 | }
|
---|
498 |
|
---|
499 | static void
|
---|
500 | GetSubSatPoint(double SatX, double SatY, double SatZ, double T,
|
---|
501 | double *Latitude, double *Longitude, double *Height)
|
---|
502 | {
|
---|
503 | double r;
|
---|
504 | /* ECD: long i; */
|
---|
505 |
|
---|
506 | r = sqrt(SQR(SatX) + SQR(SatY) + SQR(SatZ));
|
---|
507 |
|
---|
508 | *Longitude = PI2*((T-SidDay)*SiderealSolar + SidReference)
|
---|
509 | - atan2(SatY,SatX);
|
---|
510 |
|
---|
511 | /* ECD:
|
---|
512 | * want Longitude in range -PI to PI , +W
|
---|
513 | */
|
---|
514 | range (Longitude, 2*PI);
|
---|
515 | if (*Longitude > PI)
|
---|
516 | *Longitude -= 2*PI;
|
---|
517 |
|
---|
518 | *Latitude = atan(SatZ/sqrt(SQR(SatX) + SQR(SatY)));
|
---|
519 |
|
---|
520 | #if SSPELLIPSE
|
---|
521 | #else
|
---|
522 | *Height = r - EarthRadius;
|
---|
523 | #endif
|
---|
524 | }
|
---|
525 |
|
---|
526 |
|
---|
527 | #ifdef USE_ORBIT_PROPAGATOR
|
---|
528 | static void
|
---|
529 | GetPrecession(double SemiMajorAxis, double Eccentricity, double Inclination,
|
---|
530 | double *RAANPrecession, double *PerigeePrecession)
|
---|
531 | {
|
---|
532 | *RAANPrecession = 9.95*pow(EarthRadius/SemiMajorAxis,3.5) * cos(Inclination)
|
---|
533 | / SQR(1-SQR(Eccentricity)) * RadiansPerDegree;
|
---|
534 |
|
---|
535 | *PerigeePrecession = 4.97*pow(EarthRadius/SemiMajorAxis,3.5)
|
---|
536 | * (5*SQR(cos(Inclination))-1)
|
---|
537 | / SQR(1-SQR(Eccentricity)) * RadiansPerDegree;
|
---|
538 | }
|
---|
539 | #endif /* USE_ORBIT_PROPAGATOR */
|
---|
540 |
|
---|
541 | /* Compute the satellite postion and velocity in the RA based coordinate
|
---|
542 | * system.
|
---|
543 | * ECD: take care not to let Radius get below EarthRadius.
|
---|
544 | */
|
---|
545 |
|
---|
546 | static void
|
---|
547 | GetSatPosition(double EpochTime, double EpochRAAN, double EpochArgPerigee,
|
---|
548 | double SemiMajorAxis, double Inclination, double Eccentricity,
|
---|
549 | double RAANPrecession, double PerigeePrecession, double T,
|
---|
550 | double TrueAnomaly, double *X, double *Y, double *Z, double *Radius,
|
---|
551 | double *VX, double *VY, double *VZ)
|
---|
552 |
|
---|
553 | {
|
---|
554 | double RAAN,ArgPerigee;
|
---|
555 |
|
---|
556 |
|
---|
557 | double Xw,Yw,VXw,VYw; /* In orbital plane */
|
---|
558 | double Tmp;
|
---|
559 | double Px,Qx,Py,Qy,Pz,Qz; /* Escobal transformation 31 */
|
---|
560 | double CosArgPerigee,SinArgPerigee;
|
---|
561 | double CosRAAN,SinRAAN,CoSinclination,SinInclination;
|
---|
562 |
|
---|
563 | *Radius = SemiMajorAxis*(1-SQR(Eccentricity))
|
---|
564 | / (1+Eccentricity*cos(TrueAnomaly));
|
---|
565 |
|
---|
566 | if (*Radius <= EarthRadius)
|
---|
567 | *Radius = EarthRadius;
|
---|
568 |
|
---|
569 |
|
---|
570 | Xw = *Radius * cos(TrueAnomaly);
|
---|
571 | Yw = *Radius * sin(TrueAnomaly);
|
---|
572 |
|
---|
573 | Tmp = sqrt(GM/(SemiMajorAxis*(1-SQR(Eccentricity))));
|
---|
574 |
|
---|
575 | VXw = -Tmp*sin(TrueAnomaly);
|
---|
576 | VYw = Tmp*(cos(TrueAnomaly) + Eccentricity);
|
---|
577 |
|
---|
578 | ArgPerigee = EpochArgPerigee + (T-EpochTime)*PerigeePrecession;
|
---|
579 | RAAN = EpochRAAN - (T-EpochTime)*RAANPrecession;
|
---|
580 |
|
---|
581 | CosRAAN = cos(RAAN); SinRAAN = sin(RAAN);
|
---|
582 | CosArgPerigee = cos(ArgPerigee); SinArgPerigee = sin(ArgPerigee);
|
---|
583 | CoSinclination = cos(Inclination); SinInclination = sin(Inclination);
|
---|
584 |
|
---|
585 | Px = CosArgPerigee*CosRAAN - SinArgPerigee*SinRAAN*CoSinclination;
|
---|
586 | Py = CosArgPerigee*SinRAAN + SinArgPerigee*CosRAAN*CoSinclination;
|
---|
587 | Pz = SinArgPerigee*SinInclination;
|
---|
588 | Qx = -SinArgPerigee*CosRAAN - CosArgPerigee*SinRAAN*CoSinclination;
|
---|
589 | Qy = -SinArgPerigee*SinRAAN + CosArgPerigee*CosRAAN*CoSinclination;
|
---|
590 | Qz = CosArgPerigee*SinInclination;
|
---|
591 |
|
---|
592 | *X = Px*Xw + Qx*Yw; /* Escobal, transformation #31 */
|
---|
593 | *Y = Py*Xw + Qy*Yw;
|
---|
594 | *Z = Pz*Xw + Qz*Yw;
|
---|
595 |
|
---|
596 | *VX = Px*VXw + Qx*VYw;
|
---|
597 | *VY = Py*VXw + Qy*VYw;
|
---|
598 | *VZ = Pz*VXw + Qz*VYw;
|
---|
599 | }
|
---|
600 |
|
---|
601 | /* Compute the site postion and velocity in the RA based coordinate
|
---|
602 | system. SiteMatrix is set to a matrix which is used by GetTopoCentric
|
---|
603 | to convert geocentric coordinates to topocentric (observer-centered)
|
---|
604 | coordinates. */
|
---|
605 |
|
---|
606 | static void
|
---|
607 | GetSitPosition(double SiteLat, double SiteLong, double SiteElevation,
|
---|
608 | double CrntTime, double *SiteX, double *SiteY, double *SiteZ, double *SiteVX,
|
---|
609 | double *SiteVY, MAT3x3 SiteMatrix)
|
---|
610 | {
|
---|
611 | static double G1,G2; /* Used to correct for flattening of the Earth */
|
---|
612 | static double CosLat,SinLat;
|
---|
613 | static double OldSiteLat = -100000; /* Used to avoid unneccesary recomputation */
|
---|
614 | static double OldSiteElevation = -100000;
|
---|
615 | double Lat;
|
---|
616 | double SiteRA; /* Right Ascension of site */
|
---|
617 | double CosRA,SinRA;
|
---|
618 |
|
---|
619 | if ((SiteLat != OldSiteLat) || (SiteElevation != OldSiteElevation))
|
---|
620 | {
|
---|
621 | OldSiteLat = SiteLat;
|
---|
622 | OldSiteElevation = SiteElevation;
|
---|
623 | Lat = atan(1/(1-SQR(EarthFlat))*tan(SiteLat));
|
---|
624 |
|
---|
625 | CosLat = cos(Lat);
|
---|
626 | SinLat = sin(Lat);
|
---|
627 |
|
---|
628 | G1 = EarthRadius/(sqrt(1-(2*EarthFlat-SQR(EarthFlat))*SQR(SinLat)));
|
---|
629 | G2 = G1*SQR(1-EarthFlat);
|
---|
630 | G1 += SiteElevation;
|
---|
631 | G2 += SiteElevation;
|
---|
632 | }
|
---|
633 |
|
---|
634 |
|
---|
635 | SiteRA = PI2*((CrntTime-SidDay)*SiderealSolar + SidReference)
|
---|
636 | - SiteLong;
|
---|
637 | CosRA = cos(SiteRA);
|
---|
638 | SinRA = sin(SiteRA);
|
---|
639 |
|
---|
640 |
|
---|
641 | *SiteX = G1*CosLat*CosRA;
|
---|
642 | *SiteY = G1*CosLat*SinRA;
|
---|
643 | *SiteZ = G2*SinLat;
|
---|
644 | *SiteVX = -SidRate * *SiteY;
|
---|
645 | *SiteVY = SidRate * *SiteX;
|
---|
646 |
|
---|
647 | SiteMatrix[0][0] = SinLat*CosRA;
|
---|
648 | SiteMatrix[0][1] = SinLat*SinRA;
|
---|
649 | SiteMatrix[0][2] = -CosLat;
|
---|
650 | SiteMatrix[1][0] = -SinRA;
|
---|
651 | SiteMatrix[1][1] = CosRA;
|
---|
652 | SiteMatrix[1][2] = 0.0;
|
---|
653 | SiteMatrix[2][0] = CosRA*CosLat;
|
---|
654 | SiteMatrix[2][1] = SinRA*CosLat;
|
---|
655 | SiteMatrix[2][2] = SinLat;
|
---|
656 | }
|
---|
657 |
|
---|
658 | static void
|
---|
659 | GetRange(double SiteX, double SiteY, double SiteZ, double SiteVX,
|
---|
660 | double SiteVY, double SatX, double SatY, double SatZ, double SatVX,
|
---|
661 | double SatVY, double SatVZ, double *Range, double *RangeRate)
|
---|
662 | {
|
---|
663 | double DX,DY,DZ;
|
---|
664 |
|
---|
665 | DX = SatX - SiteX; DY = SatY - SiteY; DZ = SatZ - SiteZ;
|
---|
666 |
|
---|
667 | *Range = sqrt(SQR(DX)+SQR(DY)+SQR(DZ));
|
---|
668 |
|
---|
669 | *RangeRate = ((SatVX-SiteVX)*DX + (SatVY-SiteVY)*DY + SatVZ*DZ)
|
---|
670 | / *Range;
|
---|
671 | }
|
---|
672 |
|
---|
673 | /* Convert from geocentric RA based coordinates to topocentric
|
---|
674 | (observer centered) coordinates */
|
---|
675 |
|
---|
676 | static void
|
---|
677 | GetTopocentric(double SatX, double SatY, double SatZ, double SiteX,
|
---|
678 | double SiteY, double SiteZ, MAT3x3 SiteMatrix, double *X, double *Y,
|
---|
679 | double *Z)
|
---|
680 | {
|
---|
681 | SatX -= SiteX;
|
---|
682 | SatY -= SiteY;
|
---|
683 | SatZ -= SiteZ;
|
---|
684 |
|
---|
685 | *X = SiteMatrix[0][0]*SatX + SiteMatrix[0][1]*SatY
|
---|
686 | + SiteMatrix[0][2]*SatZ;
|
---|
687 | *Y = SiteMatrix[1][0]*SatX + SiteMatrix[1][1]*SatY
|
---|
688 | + SiteMatrix[1][2]*SatZ;
|
---|
689 | *Z = SiteMatrix[2][0]*SatX + SiteMatrix[2][1]*SatY
|
---|
690 | + SiteMatrix[2][2]*SatZ;
|
---|
691 | }
|
---|
692 |
|
---|
693 | static void
|
---|
694 | GetBearings(double SatX, double SatY, double SatZ, double SiteX,
|
---|
695 | double SiteY, double SiteZ, MAT3x3 SiteMatrix, double *Azimuth,
|
---|
696 | double *Elevation)
|
---|
697 | {
|
---|
698 | double x,y,z;
|
---|
699 |
|
---|
700 | GetTopocentric(SatX,SatY,SatZ,SiteX,SiteY,SiteZ,SiteMatrix,&x,&y,&z);
|
---|
701 |
|
---|
702 | *Elevation = atan(z/sqrt(SQR(x) + SQR(y)));
|
---|
703 |
|
---|
704 | *Azimuth = PI - atan2(y,x);
|
---|
705 |
|
---|
706 | if (*Azimuth < 0)
|
---|
707 | *Azimuth += PI;
|
---|
708 | }
|
---|
709 |
|
---|
710 | static int
|
---|
711 | Eclipsed(double SatX, double SatY, double SatZ, double SatRadius,
|
---|
712 | double CrntTime)
|
---|
713 | {
|
---|
714 | double MeanAnomaly,TrueAnomaly;
|
---|
715 | double SunX,SunY,SunZ,SunRad;
|
---|
716 | double vx,vy,vz;
|
---|
717 | double CosTheta;
|
---|
718 |
|
---|
719 | MeanAnomaly = SunMeanAnomaly+ (CrntTime-SunEpochTime)*SunMeanMotion*PI2;
|
---|
720 | TrueAnomaly = Kepler(MeanAnomaly,SunEccentricity);
|
---|
721 |
|
---|
722 | GetSatPosition(SunEpochTime,SunRAAN,SunArgPerigee,SunSemiMajorAxis,
|
---|
723 | SunInclination,SunEccentricity,0.0,0.0,CrntTime,
|
---|
724 | TrueAnomaly,&SunX,&SunY,&SunZ,&SunRad,&vx,&vy,&vz);
|
---|
725 |
|
---|
726 | CosTheta = (SunX*SatX + SunY*SatY + SunZ*SatZ)/(SunRad*SatRadius)
|
---|
727 | *CosPenumbra + (SatRadius/EarthRadius)*SinPenumbra;
|
---|
728 |
|
---|
729 | if (CosTheta < 0)
|
---|
730 | if (CosTheta < -sqrt(SQR(SatRadius)-SQR(EarthRadius))/SatRadius
|
---|
731 | *CosPenumbra + (SatRadius/EarthRadius)*SinPenumbra)
|
---|
732 |
|
---|
733 | return 1;
|
---|
734 | return 0;
|
---|
735 | }
|
---|
736 |
|
---|
737 | /* Initialize the Sun's keplerian elements for a given epoch.
|
---|
738 | Formulas are from "Explanatory Supplement to the Astronomical Ephemeris".
|
---|
739 | Also init the sidereal reference */
|
---|
740 |
|
---|
741 | static void
|
---|
742 | InitOrbitRoutines(double EpochDay, int AtEod)
|
---|
743 | {
|
---|
744 | double T,T2,T3,Omega;
|
---|
745 | int n;
|
---|
746 | double SunTrueAnomaly,SunDistance;
|
---|
747 |
|
---|
748 | T = (floor(EpochDay)-0.5)/36525;
|
---|
749 | T2 = T*T;
|
---|
750 | T3 = T2*T;
|
---|
751 |
|
---|
752 | SidDay = floor(EpochDay);
|
---|
753 |
|
---|
754 | SidReference = (6.6460656 + 2400.051262*T + 0.00002581*T2)/24;
|
---|
755 | SidReference -= floor(SidReference);
|
---|
756 |
|
---|
757 | /* Omega is used to correct for the nutation and the abberation */
|
---|
758 | Omega = AtEod ? (259.18 - 1934.142*T) * RadiansPerDegree : 0.0;
|
---|
759 | n = (int)(Omega / PI2);
|
---|
760 | Omega -= n*PI2;
|
---|
761 |
|
---|
762 | SunEpochTime = EpochDay;
|
---|
763 | SunRAAN = 0;
|
---|
764 |
|
---|
765 | SunInclination = (23.452294 - 0.0130125*T - 0.00000164*T2
|
---|
766 | + 0.000000503*T3 +0.00256*cos(Omega)) * RadiansPerDegree;
|
---|
767 | SunEccentricity = (0.01675104 - 0.00004180*T - 0.000000126*T2);
|
---|
768 | SunArgPerigee = (281.220833 + 1.719175*T + 0.0004527*T2
|
---|
769 | + 0.0000033*T3) * RadiansPerDegree;
|
---|
770 | SunMeanAnomaly = (358.475845 + 35999.04975*T - 0.00015*T2
|
---|
771 | - 0.00000333333*T3) * RadiansPerDegree;
|
---|
772 | n = (int)(SunMeanAnomaly / PI2);
|
---|
773 | SunMeanAnomaly -= n*PI2;
|
---|
774 |
|
---|
775 | SunMeanMotion = 1/(365.24219879 - 0.00000614*T);
|
---|
776 |
|
---|
777 | SunTrueAnomaly = Kepler(SunMeanAnomaly,SunEccentricity);
|
---|
778 | SunDistance = SunSemiMajorAxis*(1-SQR(SunEccentricity))
|
---|
779 | / (1+SunEccentricity*cos(SunTrueAnomaly));
|
---|
780 |
|
---|
781 | SinPenumbra = (SunRadius-EarthRadius)/SunDistance;
|
---|
782 | CosPenumbra = sqrt(1-SQR(SinPenumbra));
|
---|
783 | }
|
---|
784 |
|
---|
785 | /* For RCS Only -- Do Not Edit */
|
---|
786 | static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: earthsat.c,v $ $Date: 2009-07-16 10:34:37 $ $Revision: 1.9 $ $Name: not supported by cvs2svn $"};
|
---|