/[PAMELA software]/chewbacca/YodaProfiler/src/sgp4.cpp
ViewVC logotype

Contents of /chewbacca/YodaProfiler/src/sgp4.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (show annotations) (download)
Wed Aug 5 18:48:44 2009 UTC (15 years, 3 months ago) by pam-fi
Branch: MAIN
CVS Tags: v9r00, v9r01
Changes since 1.2: +1 -0 lines
Various minor modifications for compatibility with gcc 4.4, removal of warnings due to mismatch between char* and const char*, bug fix.

1 //
2 // globals.cpp
3 //
4 #include <sgp4.h>
5 #include <cstring>
6
7 //////////////////////////////////////////////////////////////////////////////
8 double sqr(const double x)
9 {
10 return (x * x);
11 }
12
13 //////////////////////////////////////////////////////////////////////////////
14 double Fmod2p(const double arg)
15 {
16 double modu = fmod(arg, TWOPI);
17
18 if (modu < 0.0)
19 modu += TWOPI;
20
21 return modu;
22 }
23
24 //////////////////////////////////////////////////////////////////////////////
25 // AcTan()
26 // ArcTangent of sin(x) / cos(x). The advantage of this function over arctan()
27 // is that it returns the correct quadrant of the angle.
28 double AcTan(const double sinx, const double cosx)
29 {
30 double ret;
31
32 if (cosx == 0.0)
33 {
34 if (sinx > 0.0)
35 ret = PI / 2.0;
36 else
37 ret = 3.0 * PI / 2.0;
38 }
39 else
40 {
41 if (cosx > 0.0)
42 ret = atan(sinx / cosx);
43 else
44 ret = PI + atan(sinx / cosx);
45 }
46
47 return ret;
48 }
49
50 //////////////////////////////////////////////////////////////////////////////
51 double rad2deg(const double r)
52 {
53 const double DEG_PER_RAD = 180.0 / PI;
54 return r * DEG_PER_RAD;
55 }
56
57 //////////////////////////////////////////////////////////////////////////////
58 double deg2rad(const double d)
59 {
60 const double RAD_PER_DEG = PI / 180.0;
61 return d * RAD_PER_DEG;
62 }
63
64 //
65 // coord.cpp
66 //
67 // Copyright (c) 2003 Michael F. Henry
68 //
69
70 //////////////////////////////////////////////////////////////////////
71 // cCoordGeo Class
72 //////////////////////////////////////////////////////////////////////
73
74 cCoordGeo::cCoordGeo()
75 {
76 m_Lat = 0.0;
77 m_Lon = 0.0;
78 m_Alt = 0.0;
79 }
80
81 //////////////////////////////////////////////////////////////////////
82 // cCoordTopo Class
83 //////////////////////////////////////////////////////////////////////
84
85 cCoordTopo::cCoordTopo()
86 {
87 m_Az = 0.0;
88 m_El = 0.0;
89 m_Range = 0.0;
90 m_RangeRate = 0.0;
91
92 }
93
94
95
96 //
97 // cVector.cpp
98 //
99 // Copyright (c) 2001-2003 Michael F. Henry
100 //
101 //*****************************************************************************
102 // Multiply each component in the vector by 'factor'.
103 //*****************************************************************************
104 void cVector::Mul(double factor)
105 {
106 m_x *= factor;
107 m_y *= factor;
108 m_z *= factor;
109 m_w *= fabs(factor);
110 }
111
112 //*****************************************************************************
113 // Subtract a vector from this one.
114 //*****************************************************************************
115 void cVector::Sub(const cVector& vec)
116 {
117 m_x -= vec.m_x;
118 m_y -= vec.m_y;
119 m_z -= vec.m_z;
120 m_w -= vec.m_w;
121 }
122
123 //*****************************************************************************
124 // Calculate the angle between this vector and another
125 //*****************************************************************************
126 double cVector::Angle(const cVector& vec) const
127 {
128 return acos(Dot(vec) / (Magnitude() * vec.Magnitude()));
129 }
130
131 //*****************************************************************************
132 //
133 //*****************************************************************************
134 double cVector::Magnitude() const
135 {
136 return sqrt((m_x * m_x) +
137 (m_y * m_y) +
138 (m_z * m_z));
139 }
140
141 //*****************************************************************************
142 // Return the dot product
143 //*****************************************************************************
144 double cVector::Dot(const cVector& vec) const
145 {
146 return (m_x * vec.m_x) +
147 (m_y * vec.m_y) +
148 (m_z * vec.m_z);
149 }
150 //
151 // cJulian.cpp
152 //
153 // This class encapsulates Julian dates with the epoch of 12:00 noon (12:00 UT)
154 // on January 1, 4713 B.C. Some epoch dates:
155 // 01/01/1990 00:00 UTC - 2447892.5
156 // 01/01/1990 12:00 UTC - 2447893.0
157 // 01/01/2000 00:00 UTC - 2451544.5
158 // 01/01/2001 00:00 UTC - 2451910.5
159 //
160 // Note the Julian day begins at noon, which allows astronomers to have all
161 // the dates in a single observing session the same.
162 //
163 // References:
164 // "Astronomical Formulae for Calculators", Jean Meeus
165 // "Satellite Communications", Dennis Roddy, 2nd Edition, 1995.
166 //
167 // Copyright (c) 2003 Michael F. Henry
168 //
169 // mfh 12/24/2003
170 //
171
172 //////////////////////////////////////////////////////////////////////////////
173 // Create a Julian date object from a time_t object. time_t objects store the
174 // number of seconds since midnight UTC January 1, 1970.
175 cJulian::cJulian(time_t time)
176 {
177 struct tm *ptm = gmtime(&time);
178 assert(ptm);
179
180 int year = ptm->tm_year + 1900;
181 double day = ptm->tm_yday + 1 +
182 (ptm->tm_hour +
183 ((ptm->tm_min +
184 (ptm->tm_sec / 60.0)) / 60.0)) / 24.0;
185
186 Initialize(year, day);
187 }
188
189 //////////////////////////////////////////////////////////////////////////////
190 // Create a Julian date object from a year and day of year.
191 // Example parameters: year = 2001, day = 1.5 (Jan 1 12h)
192 cJulian::cJulian(int year, double day)
193 {
194 Initialize(year, day);
195 }
196
197 //////////////////////////////////////////////////////////////////////////////
198 // Create a Julian date object.
199 cJulian::cJulian(int year, // i.e., 2004
200 int mon, // 1..12
201 int day, // 1..31
202 int hour, // 0..23
203 int min, // 0..59
204 double sec /* = 0.0 */) // 0..(59.999999...)
205
206 {
207 // Calculate N, the day of the year (1..366)
208 int N;
209 int F1 = (int)((275.0 * mon) / 9.0);
210 int F2 = (int)((mon + 9.0) / 12.0);
211
212 if (IsLeapYear(year))
213 {
214 // Leap year
215 N = F1 - F2 + day - 30;
216 }
217 else
218 {
219 // Common year
220 N = F1 - (2 * F2) + day - 30;
221 }
222
223 double dblDay = N + (hour + (min + (sec / 60.0)) / 60.0) / 24.0;
224
225 Initialize(year, dblDay);
226 }
227
228 //////////////////////////////////////////////////////////////////////////////
229 void cJulian::Initialize(int year, double day)
230 {
231 // 1582 A.D.: 10 days removed from calendar
232 // 3000 A.D.: Arbitrary error checking limit
233 assert((year > 1582) && (year < 3000));
234 assert((day >= 0.0) && (day <= 366.7)); // 366.5
235
236 // Now calculate Julian date
237
238 year--;
239
240 // Centuries are not leap years unless they divide by 400
241 int A = (year / 100);
242 int B = 2 - A + (A / 4);
243
244 double NewYears = (int)(365.25 * year) +
245 (int)(30.6001 * 14) +
246 1720994.5 + B; // 1720994.5 = Oct 30, year -1
247
248 m_Date = NewYears + day;
249 }
250
251 //////////////////////////////////////////////////////////////////////////////
252 // getComponent()
253 // Return requested components of date.
254 // Year : Includes the century.
255 // Month: 1..12
256 // Day : 1..31 including fractional part
257 void cJulian::getComponent(int *pYear,
258 int *pMon /* = NULL */,
259 double *pDOM /* = NULL */) const
260 {
261 assert(pYear != NULL);
262
263 double jdAdj = getDate() + 0.5;
264 int Z = (int)jdAdj; // integer part
265 double F = jdAdj - Z; // fractional part
266 double alpha = (int)((Z - 1867216.25) / 36524.25);
267 double A = Z + 1 + alpha - (int)(alpha / 4.0);
268 double B = A + 1524.0;
269 int C = (int)((B - 122.1) / 365.25);
270 int D = (int)(C * 365.25);
271 int E = (int)((B - D) / 30.6001);
272
273 double DOM = B - D - (int)(E * 30.6001) + F;
274 int month = (E < 13.5) ? (E - 1) : (E - 13);
275 int year = (month > 2.5) ? (C - 4716) : (C - 4715);
276
277 *pYear = year;
278
279 if (pMon != NULL)
280 *pMon = month;
281
282 if (pDOM != NULL)
283 *pDOM = DOM;
284 }
285
286 //////////////////////////////////////////////////////////////////////////////
287 // toGMST()
288 // Calculate Greenwich Mean Sidereal Time for the Julian date. The return value
289 // is the angle, in radians, measuring eastward from the Vernal Equinox to the
290 // prime meridian. This angle is also referred to as "ThetaG" (Theta GMST).
291 //
292 // References:
293 // The 1992 Astronomical Almanac, page B6.
294 // Explanatory Supplement to the Astronomical Almanac, page 50.
295 // Orbital Coordinate Systems, Part III, Dr. T.S. Kelso, Satellite Times,
296 // Nov/Dec 1995
297 double cJulian::toGMST() const
298 {
299 const double UT = fmod(m_Date + 0.5, 1.0);
300 const double TU = (FromJan1_12h_2000() - UT) / 36525.0;
301
302 double GMST = 24110.54841 + TU *
303 (8640184.812866 + TU * (0.093104 - TU * 6.2e-06));
304
305 GMST = fmod(GMST + SEC_PER_DAY * OMEGA_E * UT, SEC_PER_DAY);
306
307 if (GMST < 0.0)
308 GMST += SEC_PER_DAY; // "wrap" negative modulo value
309
310 return (TWOPI * (GMST / SEC_PER_DAY));
311 }
312
313 //////////////////////////////////////////////////////////////////////////////
314 // toLMST()
315 // Calculate Local Mean Sidereal Time for given longitude (for this date).
316 // The longitude is assumed to be in radians measured west from Greenwich.
317 // The return value is the angle, in radians, measuring eastward from the
318 // Vernal Equinox to the given longitude.
319 double cJulian::toLMST(double lon) const
320 {
321 return fmod(toGMST() + lon, TWOPI);
322 }
323
324 //////////////////////////////////////////////////////////////////////////////
325 // toTime()
326 // Convert to type time_t
327 // Avoid using this function as it discards the fractional seconds of the
328 // time component.
329 time_t cJulian::toTime() const
330 {
331 int nYear;
332 int nMonth;
333 double dblDay;
334
335 getComponent(&nYear, &nMonth, &dblDay);
336
337 // dblDay is the fractional Julian Day (i.e., 29.5577).
338 // Save the whole number day in nDOM and convert dblDay to
339 // the fractional portion of day.
340 int nDOM = (int)dblDay;
341
342 dblDay -= nDOM;
343
344 const int SEC_PER_MIN = 60;
345 const int SEC_PER_HR = 60 * SEC_PER_MIN;
346 const int SEC_PER_DAY = 24 * SEC_PER_HR;
347
348 int secs = (int)((dblDay * SEC_PER_DAY) + 0.5);
349
350 // Create a "struct tm" type.
351 // NOTE:
352 // The "struct tm" type has a 1-second resolution. Any fractional
353 // component of the "seconds" time value is discarded.
354 struct tm tGMT;
355 memset(&tGMT, 0, sizeof(tGMT));
356
357 tGMT.tm_year = nYear - 1900; // 2001 is 101
358 tGMT.tm_mon = nMonth - 1; // January is 0
359 tGMT.tm_mday = nDOM; // First day is 1
360 tGMT.tm_hour = secs / SEC_PER_HR;
361 tGMT.tm_min = (secs % SEC_PER_HR) / SEC_PER_MIN;
362 tGMT.tm_sec = (secs % SEC_PER_HR) % SEC_PER_MIN;
363 tGMT.tm_isdst = 0; // No conversion desired
364
365 time_t tEpoch = mktime(&tGMT);
366
367 if (tEpoch != -1)
368 {
369 // Valid time_t value returned from mktime().
370 // mktime() expects a local time which means that tEpoch now needs
371 // to be adjusted by the difference between this time zone and GMT.
372 tEpoch -= timezone;
373 }
374
375 return tEpoch;
376 }
377 //
378 // cTle.cpp
379 // This class encapsulates a single set of standard NORAD two line elements.
380 //
381 // Copyright 1996-2005 Michael F. Henry
382 //
383 // Note: The column offsets are ZERO based.
384
385 // Name
386 const int TLE_LEN_LINE_DATA = 69; const int TLE_LEN_LINE_NAME = 22;
387
388 // Line 1
389 const int TLE1_COL_SATNUM = 2; const int TLE1_LEN_SATNUM = 5;
390 const int TLE1_COL_INTLDESC_A = 9; const int TLE1_LEN_INTLDESC_A = 2;
391 const int TLE1_COL_INTLDESC_B = 11; const int TLE1_LEN_INTLDESC_B = 3;
392 const int TLE1_COL_INTLDESC_C = 14; const int TLE1_LEN_INTLDESC_C = 3;
393 const int TLE1_COL_EPOCH_A = 18; const int TLE1_LEN_EPOCH_A = 2;
394 const int TLE1_COL_EPOCH_B = 20; const int TLE1_LEN_EPOCH_B = 12;
395 const int TLE1_COL_MEANMOTIONDT = 33; const int TLE1_LEN_MEANMOTIONDT = 10;
396 const int TLE1_COL_MEANMOTIONDT2 = 44; const int TLE1_LEN_MEANMOTIONDT2 = 8;
397 const int TLE1_COL_BSTAR = 53; const int TLE1_LEN_BSTAR = 8;
398 const int TLE1_COL_EPHEMTYPE = 62; const int TLE1_LEN_EPHEMTYPE = 1;
399 const int TLE1_COL_ELNUM = 64; const int TLE1_LEN_ELNUM = 4;
400
401 // Line 2
402 const int TLE2_COL_SATNUM = 2; const int TLE2_LEN_SATNUM = 5;
403 const int TLE2_COL_INCLINATION = 8; const int TLE2_LEN_INCLINATION = 8;
404 const int TLE2_COL_RAASCENDNODE = 17; const int TLE2_LEN_RAASCENDNODE = 8;
405 const int TLE2_COL_ECCENTRICITY = 26; const int TLE2_LEN_ECCENTRICITY = 7;
406 const int TLE2_COL_ARGPERIGEE = 34; const int TLE2_LEN_ARGPERIGEE = 8;
407 const int TLE2_COL_MEANANOMALY = 43; const int TLE2_LEN_MEANANOMALY = 8;
408 const int TLE2_COL_MEANMOTION = 52; const int TLE2_LEN_MEANMOTION = 11;
409 const int TLE2_COL_REVATEPOCH = 63; const int TLE2_LEN_REVATEPOCH = 5;
410
411 /////////////////////////////////////////////////////////////////////////////
412 cTle::cTle(string& strName, string& strLine1, string& strLine2)
413 {
414 m_strName = strName;
415 m_strLine1 = strLine1;
416 m_strLine2 = strLine2;
417
418 Initialize();
419 }
420
421 /////////////////////////////////////////////////////////////////////////////
422 cTle::cTle(const cTle &tle)
423 {
424 m_strName = tle.m_strName;
425 m_strLine1 = tle.m_strLine1;
426 m_strLine2 = tle.m_strLine2;
427
428 for (int fld = FLD_FIRST; fld < FLD_LAST; fld++)
429 {
430 m_Field[fld] = tle.m_Field[fld];
431 }
432
433 m_mapCache = tle.m_mapCache;
434 }
435
436 /////////////////////////////////////////////////////////////////////////////
437 cTle::~cTle()
438 {
439 }
440
441 /////////////////////////////////////////////////////////////////////////////
442 // getField()
443 // Return requested field as a double (function return value) or as a text
444 // string (*pstr) in the units requested (eUnit). Set 'bStrUnits' to true
445 // to have units appended to text string.
446 //
447 // Note: numeric return values are cached; asking for the same field more
448 // than once incurs minimal overhead.
449 double cTle::getField(eField fld,
450 eUnits units, /* = U_NATIVE */
451 string *pstr /* = NULL */,
452 bool bStrUnits /* = false */) const
453 {
454 assert((FLD_FIRST <= fld) && (fld < FLD_LAST));
455 assert((U_FIRST <= units) && (units < U_LAST));
456
457 if (pstr)
458 {
459 // Return requested field in string form.
460 *pstr = m_Field[fld];
461
462 if (bStrUnits)
463 *pstr += getUnits(fld);
464
465 return 0.0;
466 }
467 else
468 {
469 // Return requested field in floating-point form.
470 // Return cache contents if it exists, else populate cache
471 FldKey key = Key(units, fld);
472
473 if (m_mapCache.find(key) == m_mapCache.end())
474 {
475 // Value not in cache; add it
476 double valNative = atof(m_Field[fld].c_str());
477 double valConv = ConvertUnits(valNative, fld, units);
478 m_mapCache[key] = valConv;
479
480 return valConv;
481 }
482 else
483 {
484 // return cached value
485 return m_mapCache[key];
486 }
487 }
488 }
489
490 //////////////////////////////////////////////////////////////////////////////
491 // Convert the given field into the requested units. It is assumed that
492 // the value being converted is in the TLE format's "native" form.
493 double cTle::ConvertUnits(double valNative, // value to convert
494 eField fld, // what field the value is
495 eUnits units) // what units to convert to
496 {
497 switch (fld)
498 {
499 case FLD_I:
500 case FLD_RAAN:
501 case FLD_ARGPER:
502 case FLD_M:
503 {
504 // The native TLE format is DEGREES
505 if (units == U_RAD)
506 return valNative * RADS_PER_DEG;
507 }
508
509 case FLD_NORADNUM:
510 case FLD_INTLDESC:
511 case FLD_SET:
512 case FLD_EPOCHYEAR:
513 case FLD_EPOCHDAY:
514 case FLD_ORBITNUM:
515 case FLD_E:
516 case FLD_MMOTION:
517 case FLD_MMOTIONDT:
518 case FLD_MMOTIONDT2:
519 case FLD_BSTAR:
520 case FLD_LAST:
521 { // do nothing
522
523 }
524
525 }
526
527 return valNative; // return value in unconverted native format
528 }
529
530 //////////////////////////////////////////////////////////////////////////////
531 string cTle::getUnits(eField fld) const
532 {
533 static const string strDegrees = " degrees";
534 static const string strRevsPerDay = " revs / day";
535 static const string strNull;
536
537 switch (fld)
538 {
539 case FLD_I:
540 case FLD_RAAN:
541 case FLD_ARGPER:
542 case FLD_M:
543 return strDegrees;
544
545 case FLD_MMOTION:
546 return strRevsPerDay;
547
548 default:
549 return strNull;
550 }
551 }
552
553 /////////////////////////////////////////////////////////////////////////////
554 // ExpToDecimal()
555 // Converts TLE-style exponential notation of the form [ |-]00000[+|-]0 to
556 // decimal notation. Assumes implied decimal point to the left of the first
557 // number in the string, i.e.,
558 // " 12345-3" = 0.00012345
559 // "-23429-5" = -0.0000023429
560 // " 40436+1" = 4.0436
561 string cTle::ExpToDecimal(const string &str)
562 {
563 const int COL_EXP_SIGN = 6;
564 const int LEN_EXP = 2;
565
566 const int LEN_BUFREAL = 32; // max length of buffer to hold floating point
567 // representation of input string.
568 int nMan;
569 int nExp;
570
571 // sscanf(%d) will read up to the exponent sign
572 sscanf(str.c_str(), "%d", &nMan);
573
574 double dblMan = nMan;
575 bool bNeg = (nMan < 0);
576
577 if (bNeg)
578 dblMan *= -1;
579
580 // Move decimal place to left of first digit
581 while (dblMan >= 1.0)
582 dblMan /= 10.0;
583
584 if (bNeg)
585 dblMan *= -1;
586
587 // now read exponent
588 sscanf(str.substr(COL_EXP_SIGN, LEN_EXP).c_str(), "%d", &nExp);
589
590 double dblVal = dblMan * pow(10.0, nExp);
591 char szVal[LEN_BUFREAL];
592
593 snprintf(szVal, sizeof(szVal), "%.9f", dblVal);
594
595 string strVal = szVal;
596
597 return strVal;
598
599 } // ExpToDecimal()
600
601 /////////////////////////////////////////////////////////////////////////////
602 // Initialize()
603 // Initialize the string array.
604 void cTle::Initialize()
605 {
606 // Have we already been initialized?
607 if (m_Field[FLD_NORADNUM].size())
608 return;
609
610 assert(!m_strName.empty());
611 assert(!m_strLine1.empty());
612 assert(!m_strLine2.empty());
613
614 m_Field[FLD_NORADNUM] = m_strLine1.substr(TLE1_COL_SATNUM, TLE1_LEN_SATNUM);
615 m_Field[FLD_INTLDESC] = m_strLine1.substr(TLE1_COL_INTLDESC_A,
616 TLE1_LEN_INTLDESC_A +
617 TLE1_LEN_INTLDESC_B +
618 TLE1_LEN_INTLDESC_C);
619 m_Field[FLD_EPOCHYEAR] =
620 m_strLine1.substr(TLE1_COL_EPOCH_A, TLE1_LEN_EPOCH_A);
621
622 m_Field[FLD_EPOCHDAY] =
623 m_strLine1.substr(TLE1_COL_EPOCH_B, TLE1_LEN_EPOCH_B);
624
625 if (m_strLine1[TLE1_COL_MEANMOTIONDT] == '-')
626 {
627 // value is negative
628 m_Field[FLD_MMOTIONDT] = "-0";
629 }
630 else
631 m_Field[FLD_MMOTIONDT] = "0";
632
633 m_Field[FLD_MMOTIONDT] += m_strLine1.substr(TLE1_COL_MEANMOTIONDT + 1,
634 TLE1_LEN_MEANMOTIONDT);
635
636 // decimal point assumed; exponential notation
637 m_Field[FLD_MMOTIONDT2] = ExpToDecimal(
638 m_strLine1.substr(TLE1_COL_MEANMOTIONDT2,
639 TLE1_LEN_MEANMOTIONDT2));
640 // decimal point assumed; exponential notation
641 m_Field[FLD_BSTAR] = ExpToDecimal(m_strLine1.substr(TLE1_COL_BSTAR,
642 TLE1_LEN_BSTAR));
643 //TLE1_COL_EPHEMTYPE
644 //TLE1_LEN_EPHEMTYPE
645 m_Field[FLD_SET] = m_strLine1.substr(TLE1_COL_ELNUM, TLE1_LEN_ELNUM);
646
647 TrimLeft(m_Field[FLD_SET]);
648
649 //TLE2_COL_SATNUM
650 //TLE2_LEN_SATNUM
651
652 m_Field[FLD_I] = m_strLine2.substr(TLE2_COL_INCLINATION,
653 TLE2_LEN_INCLINATION);
654 TrimLeft(m_Field[FLD_I]);
655
656 m_Field[FLD_RAAN] = m_strLine2.substr(TLE2_COL_RAASCENDNODE,
657 TLE2_LEN_RAASCENDNODE);
658 TrimLeft(m_Field[FLD_RAAN]);
659
660 // decimal point is assumed
661 m_Field[FLD_E] = "0.";
662 m_Field[FLD_E] += m_strLine2.substr(TLE2_COL_ECCENTRICITY,
663 TLE2_LEN_ECCENTRICITY);
664
665 m_Field[FLD_ARGPER] = m_strLine2.substr(TLE2_COL_ARGPERIGEE,
666 TLE2_LEN_ARGPERIGEE);
667 TrimLeft(m_Field[FLD_ARGPER]);
668
669 m_Field[FLD_M] = m_strLine2.substr(TLE2_COL_MEANANOMALY,
670 TLE2_LEN_MEANANOMALY);
671 TrimLeft(m_Field[FLD_M]);
672
673 m_Field[FLD_MMOTION] = m_strLine2.substr(TLE2_COL_MEANMOTION,
674 TLE2_LEN_MEANMOTION);
675 TrimLeft(m_Field[FLD_MMOTION]);
676
677 m_Field[FLD_ORBITNUM] = m_strLine2.substr(TLE2_COL_REVATEPOCH,
678 TLE2_LEN_REVATEPOCH);
679 TrimLeft(m_Field[FLD_ORBITNUM]);
680
681 } // InitStrVars()
682
683 /////////////////////////////////////////////////////////////////////////////
684 // IsTleFormat()
685 // Returns true if "str" is a valid data line of a two-line element set,
686 // else false.
687 //
688 // To be valid a line must:
689 // Have as the first character the line number
690 // Have as the second character a blank
691 // Be TLE_LEN_LINE_DATA characters long
692 // Have a valid checksum (note: no longer required as of 12/96)
693 //
694 bool cTle::IsValidLine(string& str, eTleLine line)
695 {
696 TrimLeft(str);
697 TrimRight(str);
698
699 size_t nLen = str.size();
700
701 if (nLen != (uint)TLE_LEN_LINE_DATA)
702 return false;
703
704 // First char in string must be line number
705 if ((str[0] - '0') != line)
706 return false;
707
708 // Second char in string must be blank
709 if (str[1] != ' ')
710 return false;
711
712 /*
713 NOTE: 12/96
714 The requirement that the last char in the line data must be a valid
715 checksum is too restrictive.
716
717 // Last char in string must be checksum
718 int nSum = CheckSum(str);
719
720 if (nSum != (str[TLE_LEN_LINE_DATA - 1] - '0'))
721 return false;
722 */
723
724 return true;
725
726 } // IsTleFormat()
727
728 /////////////////////////////////////////////////////////////////////////////
729 // CheckSum()
730 // Calculate the check sum for a given line of TLE data, the last character
731 // of which is the current checksum. (Although there is no check here,
732 // the current checksum should match the one we calculate.)
733 // The checksum algorithm:
734 // Each number in the data line is summed, modulo 10.
735 // Non-numeric characters are zero, except minus signs, which are 1.
736 //
737 int cTle::CheckSum(const string& str)
738 {
739 // The length is "- 1" because we don't include the current (existing)
740 // checksum character in the checksum calculation.
741 size_t len = str.size() - 1;
742 int xsum = 0;
743
744 for (size_t i = 0; i < len; i++)
745 {
746 char ch = str[i];
747 if (isdigit(ch))
748 xsum += (ch - '0');
749 else if (ch == '-')
750 xsum++;
751 }
752
753 return (xsum % 10);
754
755 } // CheckSum()
756
757 /////////////////////////////////////////////////////////////////////////////
758 void cTle::TrimLeft(string& s)
759 {
760 while (s[0] == ' ')
761 s.erase(0, 1);
762 }
763
764 /////////////////////////////////////////////////////////////////////////////
765 void cTle::TrimRight(string& s)
766 {
767 while (s[s.size() - 1] == ' ')
768 s.erase(s.size() - 1);
769 }
770
771 //
772 // cEci.cpp
773 //
774 // Copyright (c) 2002-2003 Michael F. Henry
775 //
776 //////////////////////////////////////////////////////////////////////
777 // cEci Class
778 //////////////////////////////////////////////////////////////////////
779 cEci::cEci(const cVector &pos,
780 const cVector &vel,
781 const cJulian &date,
782 bool IsAeUnits /* = true */)
783 {
784 m_pos = pos;
785 m_vel = vel;
786 m_date = date;
787 m_VecUnits = (IsAeUnits ? UNITS_AE : UNITS_NONE);
788 }
789
790 //////////////////////////////////////////////////////////////////////
791 // cEci(cCoordGeo&, cJulian&)
792 // Calculate the ECI coordinates of the location "geo" at time "date".
793 // Assumes geo coordinates are km-based.
794 // Assumes the earth is an oblate spheroid as defined in WGS '72.
795 // Reference: The 1992 Astronomical Almanac, page K11
796 // Reference: www.celestrak.com (Dr. TS Kelso)
797 cEci::cEci(const cCoordGeo &geo, const cJulian &date)
798 {
799 m_VecUnits = UNITS_KM;
800
801 double mfactor = TWOPI * (OMEGA_E / SEC_PER_DAY);
802 double lat = geo.m_Lat;
803 double lon = geo.m_Lon;
804 double alt = geo.m_Alt;
805
806 // Calculate Local Mean Sidereal Time (theta)
807 double theta = date.toLMST(lon);
808 double c = 1.0 / sqrt(1.0 + F * (F - 2.0) * sqr(sin(lat)));
809 double s = sqr(1.0 - F) * c;
810 double achcp = (XKMPER_WGS72 * c + alt) * cos(lat);
811
812 m_date = date;
813
814 m_pos.m_x = achcp * cos(theta); // km
815 m_pos.m_y = achcp * sin(theta); // km
816 m_pos.m_z = (XKMPER_WGS72 * s + alt) * sin(lat); // km
817 m_pos.m_w = sqrt(sqr(m_pos.m_x) +
818 sqr(m_pos.m_y) +
819 sqr(m_pos.m_z)); // range, km
820
821 m_vel.m_x = -mfactor * m_pos.m_y; // km / sec
822 m_vel.m_y = mfactor * m_pos.m_x;
823 m_vel.m_z = 0.0;
824 m_vel.m_w = sqrt(sqr(m_vel.m_x) + // range rate km/sec^2
825 sqr(m_vel.m_y));
826 }
827
828 //////////////////////////////////////////////////////////////////////////////
829 // toGeo()
830 // Return the corresponding geodetic position (based on the current ECI
831 // coordinates/Julian date).
832 // Assumes the earth is an oblate spheroid as defined in WGS '72.
833 // Side effects: Converts the position and velocity vectors to km-based units.
834 // Reference: The 1992 Astronomical Almanac, page K12.
835 // Reference: www.celestrak.com (Dr. TS Kelso)
836 cCoordGeo cEci::toGeo()
837 {
838 ae2km(); // Vectors must be in kilometer-based units
839
840 double theta = AcTan(m_pos.m_y, m_pos.m_x);
841 double lon = fmod(theta - m_date.toGMST(), TWOPI);
842
843 if (lon < 0.0)
844 lon += TWOPI; // "wrap" negative modulo
845
846 double r = sqrt(sqr(m_pos.m_x) + sqr(m_pos.m_y));
847 double e2 = F * (2.0 - F);
848 double lat = AcTan(m_pos.m_z, r);
849
850 const double delta = 1.0e-07;
851 double phi;
852 double c;
853
854 do
855 {
856 phi = lat;
857 c = 1.0 / sqrt(1.0 - e2 * sqr(sin(phi)));
858 lat = AcTan(m_pos.m_z + XKMPER_WGS72 * c * e2 * sin(phi), r);
859 }
860 while (fabs(lat - phi) > delta);
861
862 double alt = r / cos(lat) - XKMPER_WGS72 * c;
863
864 return cCoordGeo(lat, lon, alt); // radians, radians, kilometers
865 }
866
867 //////////////////////////////////////////////////////////////////////////////
868 // ae2km()
869 // Convert the position and velocity vector units from AE-based units
870 // to kilometer based units.
871 void cEci::ae2km()
872 {
873 if (UnitsAreAe())
874 {
875 MulPos(XKMPER_WGS72 / AE); // km
876 MulVel((XKMPER_WGS72 / AE) * (MIN_PER_DAY / 86400)); // km/sec
877 m_VecUnits = UNITS_KM;
878 }
879 }
880 //
881 // cNoradBase.cpp
882 //
883 // Historical Note:
884 // The equations used here (and in derived classes) to determine satellite
885 // ECI coordinates/velocity come from the December, 1980 NORAD document
886 // "Space Track Report No. 3". The report details 6 orbital models and
887 // provides FORTRAN IV implementations of each. The classes here
888 // implement only two of the orbital models: SGP4 and SDP4. These two models,
889 // one for "near-earth" objects and one for "deep space" objects, are widely
890 // used in satellite tracking software and can produce very accurate results
891 // when used with current NORAD two-line element datum.
892 //
893 // The NORAD FORTRAN IV SGP4/SDP4 implementations were converted to Pascal by
894 // Dr. TS Kelso in 1995. In 1996 these routines were ported in a straight-
895 // forward manner to C++ by Varol Okan. The SGP4/SDP4 classes here were
896 // written by Michael F. Henry in 2002-03 and are a modern C++ re-write of
897 // the work done by Okan. In addition to introducing an object-oriented
898 // architecture, the last residues of the original FORTRAN code (such as
899 // labels and gotos) were eradicated.
900 //
901 // For excellent information on the underlying physics of orbits, visible
902 // satellite observations, current NORAD TLE data, and other related material,
903 // see http://www.celestrak.com which is maintained by Dr. TS Kelso.
904 //
905 // Copyright (c) 2003 Michael F. Henry
906 //
907 // mfh 12/07/2003
908 //
909 //////////////////////////////////////////////////////////////////////////////
910 cNoradBase::cNoradBase(const cOrbit &orbit) :
911 m_Orbit(orbit)
912 {
913 Initialize();
914 }
915
916 cNoradBase& cNoradBase::operator=(const cNoradBase &b)
917 {
918 // m_Orbit is a "const" member var, so cast away its
919 // "const-ness" in order to complete the assigment.
920 *(const_cast<cOrbit*>(&m_Orbit)) = b.m_Orbit;
921
922 return *this;
923 }
924
925 //////////////////////////////////////////////////////////////////////////////
926 // Initialize()
927 // Perform the initialization of member variables, specifically the variables
928 // used by derived-class objects to calculate ECI coordinates.
929 void cNoradBase::Initialize()
930 {
931 // Initialize any variables which are time-independent when
932 // calculating the ECI coordinates of the satellite.
933 m_satInc = m_Orbit.Inclination();
934 m_satEcc = m_Orbit.Eccentricity();
935
936 m_cosio = cos(m_satInc);
937 m_theta2 = m_cosio * m_cosio;
938 m_x3thm1 = 3.0 * m_theta2 - 1.0;
939 m_eosq = m_satEcc * m_satEcc;
940 m_betao2 = 1.0 - m_eosq;
941 m_betao = sqrt(m_betao2);
942
943 // The "recovered" semi-minor axis and mean motion.
944 m_aodp = m_Orbit.SemiMinor();
945 m_xnodp = m_Orbit.mnMotionRec();
946
947 // For perigee below 156 km, the values of S and QOMS2T are altered.
948 m_perigee = XKMPER_WGS72 * (m_aodp * (1.0 - m_satEcc) - AE);
949
950 m_s4 = S;
951 m_qoms24 = QOMS2T;
952
953 if (m_perigee < 156.0)
954 {
955 m_s4 = m_perigee - 78.0;
956
957 if (m_perigee <= 98.0)
958 {
959 m_s4 = 20.0;
960 }
961
962 m_qoms24 = pow((120.0 - m_s4) * AE / XKMPER_WGS72, 4.0);
963 m_s4 = m_s4 / XKMPER_WGS72 + AE;
964 }
965
966 const double pinvsq = 1.0 / (m_aodp * m_aodp * m_betao2 * m_betao2);
967
968 m_tsi = 1.0 / (m_aodp - m_s4);
969 m_eta = m_aodp * m_satEcc * m_tsi;
970 m_etasq = m_eta * m_eta;
971 m_eeta = m_satEcc * m_eta;
972
973 const double psisq = fabs(1.0 - m_etasq);
974
975 m_coef = m_qoms24 * pow(m_tsi,4.0);
976 m_coef1 = m_coef / pow(psisq,3.5);
977
978 const double c2 = m_coef1 * m_xnodp *
979 (m_aodp * (1.0 + 1.5 * m_etasq + m_eeta * (4.0 + m_etasq)) +
980 0.75 * CK2 * m_tsi / psisq * m_x3thm1 *
981 (8.0 + 3.0 * m_etasq * (8.0 + m_etasq)));
982
983 m_c1 = m_Orbit.BStar() * c2;
984 m_sinio = sin(m_satInc);
985
986 const double a3ovk2 = -XJ3 / CK2 * pow(AE,3.0);
987
988 m_c3 = m_coef * m_tsi * a3ovk2 * m_xnodp * AE * m_sinio / m_satEcc;
989 m_x1mth2 = 1.0 - m_theta2;
990 m_c4 = 2.0 * m_xnodp * m_coef1 * m_aodp * m_betao2 *
991 (m_eta * (2.0 + 0.5 * m_etasq) +
992 m_satEcc * (0.5 + 2.0 * m_etasq) -
993 2.0 * CK2 * m_tsi / (m_aodp * psisq) *
994 (-3.0 * m_x3thm1 * (1.0 - 2.0 * m_eeta + m_etasq * (1.5 - 0.5 * m_eeta)) +
995 0.75 * m_x1mth2 *
996 (2.0 * m_etasq - m_eeta * (1.0 + m_etasq)) *
997 cos(2.0 * m_Orbit.ArgPerigee())));
998
999 const double theta4 = m_theta2 * m_theta2;
1000 const double temp1 = 3.0 * CK2 * pinvsq * m_xnodp;
1001 const double temp2 = temp1 * CK2 * pinvsq;
1002 const double temp3 = 1.25 * CK4 * pinvsq * pinvsq * m_xnodp;
1003
1004 m_xmdot = m_xnodp + 0.5 * temp1 * m_betao * m_x3thm1 +
1005 0.0625 * temp2 * m_betao *
1006 (13.0 - 78.0 * m_theta2 + 137.0 * theta4);
1007
1008 const double x1m5th = 1.0 - 5.0 * m_theta2;
1009
1010 m_omgdot = -0.5 * temp1 * x1m5th + 0.0625 * temp2 *
1011 (7.0 - 114.0 * m_theta2 + 395.0 * theta4) +
1012 temp3 * (3.0 - 36.0 * m_theta2 + 49.0 * theta4);
1013
1014 const double xhdot1 = -temp1 * m_cosio;
1015
1016 m_xnodot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * m_theta2) +
1017 2.0 * temp3 * (3.0 - 7.0 * m_theta2)) * m_cosio;
1018 m_xnodcf = 3.5 * m_betao2 * xhdot1 * m_c1;
1019 m_t2cof = 1.5 * m_c1;
1020 m_xlcof = 0.125 * a3ovk2 * m_sinio *
1021 (3.0 + 5.0 * m_cosio) / (1.0 + m_cosio);
1022 m_aycof = 0.25 * a3ovk2 * m_sinio;
1023 m_x7thm1 = 7.0 * m_theta2 - 1.0;
1024 }
1025
1026 //////////////////////////////////////////////////////////////////////////////
1027 bool cNoradBase::FinalPosition(double incl, double omega,
1028 double e, double a,
1029 double xl, double xnode,
1030 double xn, double tsince,
1031 cEci &eci)
1032 {
1033 if ((e * e) > 1.0)
1034 {
1035 // error in satellite data
1036 return false;
1037 }
1038
1039 double beta = sqrt(1.0 - e * e);
1040
1041 // Long period periodics
1042 double axn = e * cos(omega);
1043 double temp = 1.0 / (a * beta * beta);
1044 double xll = temp * m_xlcof * axn;
1045 double aynl = temp * m_aycof;
1046 double xlt = xl + xll;
1047 double ayn = e * sin(omega) + aynl;
1048
1049 // Solve Kepler's Equation
1050
1051 double capu = Fmod2p(xlt - xnode);
1052 double temp2 = capu;
1053 double temp3 = 0.0;
1054 double temp4 = 0.0;
1055 double temp5 = 0.0;
1056 double temp6 = 0.0;
1057 double sinepw = 0.0;
1058 double cosepw = 0.0;
1059 bool fDone = false;
1060
1061 for (int i = 1; (i <= 10) && !fDone; i++)
1062 {
1063 sinepw = sin(temp2);
1064 cosepw = cos(temp2);
1065 temp3 = axn * sinepw;
1066 temp4 = ayn * cosepw;
1067 temp5 = axn * cosepw;
1068 temp6 = ayn * sinepw;
1069
1070 double epw = (capu - temp4 + temp3 - temp2) /
1071 (1.0 - temp5 - temp6) + temp2;
1072
1073 if (fabs(epw - temp2) <= E6A)
1074 fDone = true;
1075 else
1076 temp2 = epw;
1077 }
1078
1079 // Short period preliminary quantities
1080 double ecose = temp5 + temp6;
1081 double esine = temp3 - temp4;
1082 double elsq = axn * axn + ayn * ayn;
1083 temp = 1.0 - elsq;
1084 double pl = a * temp;
1085 double r = a * (1.0 - ecose);
1086 double temp1 = 1.0 / r;
1087 double rdot = XKE * sqrt(a) * esine * temp1;
1088 double rfdot = XKE * sqrt(pl) * temp1;
1089 temp2 = a * temp1;
1090 double betal = sqrt(temp);
1091 temp3 = 1.0 / (1.0 + betal);
1092 double cosu = temp2 * (cosepw - axn + ayn * esine * temp3);
1093 double sinu = temp2 * (sinepw - ayn - axn * esine * temp3);
1094 double u = AcTan(sinu, cosu);
1095 double sin2u = 2.0 * sinu * cosu;
1096 double cos2u = 2.0 * cosu * cosu - 1.0;
1097
1098 temp = 1.0 / pl;
1099 temp1 = CK2 * temp;
1100 temp2 = temp1 * temp;
1101
1102 // Update for short periodics
1103 double rk = r * (1.0 - 1.5 * temp2 * betal * m_x3thm1) +
1104 0.5 * temp1 * m_x1mth2 * cos2u;
1105 double uk = u - 0.25 * temp2 * m_x7thm1 * sin2u;
1106 double xnodek = xnode + 1.5 * temp2 * m_cosio * sin2u;
1107 double xinck = incl + 1.5 * temp2 * m_cosio * m_sinio * cos2u;
1108 double rdotk = rdot - xn * temp1 * m_x1mth2 * sin2u;
1109 double rfdotk = rfdot + xn * temp1 * (m_x1mth2 * cos2u + 1.5 * m_x3thm1);
1110
1111 // Orientation vectors
1112 double sinuk = sin(uk);
1113 double cosuk = cos(uk);
1114 double sinik = sin(xinck);
1115 double cosik = cos(xinck);
1116 double sinnok = sin(xnodek);
1117 double cosnok = cos(xnodek);
1118 double xmx = -sinnok * cosik;
1119 double xmy = cosnok * cosik;
1120 double ux = xmx * sinuk + cosnok * cosuk;
1121 double uy = xmy * sinuk + sinnok * cosuk;
1122 double uz = sinik * sinuk;
1123 double vx = xmx * cosuk - cosnok * sinuk;
1124 double vy = xmy * cosuk - sinnok * sinuk;
1125 double vz = sinik * cosuk;
1126
1127 // Position
1128 double x = rk * ux;
1129 double y = rk * uy;
1130 double z = rk * uz;
1131
1132 cVector vecPos(x, y, z);
1133
1134 // Validate on altitude
1135 double altKm = (vecPos.Magnitude() * (XKMPER_WGS72 / AE));
1136
1137 if ((altKm < XKMPER_WGS72) || (altKm > (2 * GEOSYNC_ALT)))
1138 return false;
1139
1140 // Velocity
1141 double xdot = rdotk * ux + rfdotk * vx;
1142 double ydot = rdotk * uy + rfdotk * vy;
1143 double zdot = rdotk * uz + rfdotk * vz;
1144
1145 cVector vecVel(xdot, ydot, zdot);
1146
1147 cJulian gmt = m_Orbit.Epoch();
1148 gmt.addMin(tsince);
1149
1150 eci = cEci(vecPos, vecVel, gmt);
1151
1152 return true;
1153 }
1154
1155 //
1156 // cNoradSGP4.cpp
1157 //
1158 // NORAD SGP4 implementation. See historical note in cNoradBase.cpp
1159 // Copyright (c) 2003 Michael F. Henry
1160 //
1161 // mfh 12/07/2003
1162 //
1163 //////////////////////////////////////////////////////////////////////////////
1164 cNoradSGP4::cNoradSGP4(const cOrbit &orbit) :
1165 cNoradBase(orbit)
1166 {
1167 m_c5 = 2.0 * m_coef1 * m_aodp * m_betao2 *
1168 (1.0 + 2.75 * (m_etasq + m_eeta) + m_eeta * m_etasq);
1169 m_omgcof = m_Orbit.BStar() * m_c3 * cos(m_Orbit.ArgPerigee());
1170 m_xmcof = -TWOTHRD * m_coef * m_Orbit.BStar() * AE / m_eeta;
1171 m_delmo = pow(1.0 + m_eta * cos(m_Orbit.mnAnomaly()), 3.0);
1172 m_sinmo = sin(m_Orbit.mnAnomaly());
1173 }
1174
1175
1176 //////////////////////////////////////////////////////////////////////////////
1177 // getPosition()
1178 // This procedure returns the ECI position and velocity for the satellite
1179 // in the orbit at the given number of minutes since the TLE epoch time
1180 // using the NORAD Simplified General Perturbation 4, near earth orbit
1181 // model.
1182 //
1183 // tsince - Time in minutes since the TLE epoch (GMT).
1184 // eci - ECI object to hold position information.
1185 // To convert the returned ECI position vector to km,
1186 // multiply each component by:
1187 // (XKMPER_WGS72 / AE).
1188 // To convert the returned ECI velocity vector to km/sec,
1189 // multiply each component by:
1190 // (XKMPER_WGS72 / AE) * (MIN_PER_DAY / 86400).
1191
1192 bool cNoradSGP4::getPosition(double tsince, cEci &eci)
1193 {
1194 // For m_perigee less than 220 kilometers, the isimp flag is set and
1195 // the equations are truncated to linear variation in sqrt a and
1196 // quadratic variation in mean anomaly. Also, the m_c3 term, the
1197 // delta omega term, and the delta m term are dropped.
1198 bool isimp = false;
1199 if ((m_aodp * (1.0 - m_satEcc) / AE) < (220.0 / XKMPER_WGS72 + AE))
1200 {
1201 isimp = true;
1202 }
1203
1204 double d2 = 0.0;
1205 double d3 = 0.0;
1206 double d4 = 0.0;
1207
1208 double t3cof = 0.0;
1209 double t4cof = 0.0;
1210 double t5cof = 0.0;
1211
1212 if (!isimp)
1213 {
1214 double c1sq = m_c1 * m_c1;
1215
1216 d2 = 4.0 * m_aodp * m_tsi * c1sq;
1217
1218 double temp = d2 * m_tsi * m_c1 / 3.0;
1219
1220 d3 = (17.0 * m_aodp + m_s4) * temp;
1221 d4 = 0.5 * temp * m_aodp * m_tsi *
1222 (221.0 * m_aodp + 31.0 * m_s4) * m_c1;
1223 t3cof = d2 + 2.0 * c1sq;
1224 t4cof = 0.25 * (3.0 * d3 + m_c1 * (12.0 * d2 + 10.0 * c1sq));
1225 t5cof = 0.2 * (3.0 * d4 + 12.0 * m_c1 * d3 + 6.0 *
1226 d2 * d2 + 15.0 * c1sq * (2.0 * d2 + c1sq));
1227 }
1228
1229 // Update for secular gravity and atmospheric drag.
1230 double xmdf = m_Orbit.mnAnomaly() + m_xmdot * tsince;
1231 double omgadf = m_Orbit.ArgPerigee() + m_omgdot * tsince;
1232 double xnoddf = m_Orbit.RAAN() + m_xnodot * tsince;
1233 double omega = omgadf;
1234 double xmp = xmdf;
1235 double tsq = tsince * tsince;
1236 double xnode = xnoddf + m_xnodcf * tsq;
1237 double tempa = 1.0 - m_c1 * tsince;
1238 double tempe = m_Orbit.BStar() * m_c4 * tsince;
1239 double templ = m_t2cof * tsq;
1240
1241 if (!isimp)
1242 {
1243 double delomg = m_omgcof * tsince;
1244 double delm = m_xmcof * (pow(1.0 + m_eta * cos(xmdf), 3.0) - m_delmo);
1245 double temp = delomg + delm;
1246
1247 xmp = xmdf + temp;
1248 omega = omgadf - temp;
1249
1250 double tcube = tsq * tsince;
1251 double tfour = tsince * tcube;
1252
1253 tempa = tempa - d2 * tsq - d3 * tcube - d4 * tfour;
1254 tempe = tempe + m_Orbit.BStar() * m_c5 * (sin(xmp) - m_sinmo);
1255 templ = templ + t3cof * tcube + tfour * (t4cof + tsince * t5cof);
1256 }
1257
1258 double a = m_aodp * sqr(tempa);
1259 double e = m_satEcc - tempe;
1260
1261
1262 double xl = xmp + omega + xnode + m_xnodp * templ;
1263 double xn = XKE / pow(a, 1.5);
1264
1265 return FinalPosition(m_satInc, omgadf, e, a, xl, xnode, xn, tsince, eci);
1266 }
1267
1268 //
1269 // cNoradSDP4.cpp
1270 //
1271 // NORAD SDP4 implementation. See historical note in cNoradBase.cpp
1272 // Copyright (c) 2003 Michael F. Henry
1273 //
1274 // mfh 12/07/2003
1275 //
1276
1277 const double zns = 1.19459E-5; const double c1ss = 2.9864797E-6;
1278 const double zes = 0.01675; const double znl = 1.5835218E-4;
1279 const double c1l = 4.7968065E-7; const double zel = 0.05490;
1280 const double zcosis = 0.91744867; const double zsinis = 0.39785416;
1281 const double zsings = -0.98088458; const double zcosgs = 0.1945905;
1282 const double q22 = 1.7891679E-6; const double q31 = 2.1460748E-6;
1283 const double q33 = 2.2123015E-7; const double g22 = 5.7686396;
1284 const double g32 = 0.95240898; const double g44 = 1.8014998;
1285 const double g52 = 1.0508330; const double g54 = 4.4108898;
1286 const double root22 = 1.7891679E-6; const double root32 = 3.7393792E-7;
1287 const double root44 = 7.3636953E-9; const double root52 = 1.1428639E-7;
1288 const double root54 = 2.1765803E-9; const double thdt = 4.3752691E-3;
1289
1290 //////////////////////////////////////////////////////////////////////////////
1291 cNoradSDP4::cNoradSDP4(const cOrbit &orbit) :
1292 cNoradBase(orbit)
1293 {
1294 m_sing = sin(m_Orbit.ArgPerigee());
1295 m_cosg = cos(m_Orbit.ArgPerigee());
1296
1297 dp_savtsn = 0.0;
1298 dp_zmos = 0.0;
1299 dp_se2 = 0.0;
1300 dp_se3 = 0.0;
1301 dp_si2 = 0.0;
1302 dp_si3 = 0.0;
1303 dp_sl2 = 0.0;
1304 dp_sl3 = 0.0;
1305 dp_sl4 = 0.0;
1306 dp_sghs = 0.0;
1307 dp_sgh2 = 0.0;
1308 dp_sgh3 = 0.0;
1309 dp_sgh4 = 0.0;
1310 dp_sh2 = 0.0;
1311 dp_sh3 = 0.0;
1312 dp_zmol = 0.0;
1313 dp_ee2 = 0.0;
1314 dp_e3 = 0.0;
1315 dp_xi2 = 0.0;
1316 dp_xi3 = 0.0;
1317 dp_xl2 = 0.0;
1318 dp_xl3 = 0.0;
1319 dp_xl4 = 0.0;
1320 dp_xgh2 = 0.0;
1321 dp_xgh3 = 0.0;
1322 dp_xgh4 = 0.0;
1323 dp_xh2 = 0.0;
1324 dp_xh3 = 0.0;
1325 dp_xqncl = 0.0;
1326 dp_thgr = 0.0;
1327 dp_omegaq = 0.0;
1328 dp_sse = 0.0;
1329 dp_ssi = 0.0;
1330 dp_ssl = 0.0;
1331 dp_ssh = 0.0;
1332 dp_ssg = 0.0;
1333 dp_d2201 = 0.0;
1334 dp_d2211 = 0.0;
1335 dp_d3210 = 0.0;
1336 dp_d3222 = 0.0;
1337 dp_d4410 = 0.0;
1338 dp_d4422 = 0.0;
1339 dp_d5220 = 0.0;
1340 dp_d5232 = 0.0;
1341 dp_d5421 = 0.0;
1342 dp_d5433 = 0.0;
1343 dp_xlamo = 0.0;
1344 dp_del1 = 0.0;
1345 dp_del2 = 0.0;
1346 dp_del3 = 0.0;
1347 dp_fasx2 = 0.0;
1348 dp_fasx4 = 0.0;
1349 dp_fasx6 = 0.0;
1350 dp_xfact = 0.0;
1351 dp_xli = 0.0;
1352 dp_xni = 0.0;
1353 dp_atime = 0.0;
1354 dp_stepp = 0.0;
1355 dp_stepn = 0.0;
1356 dp_step2 = 0.0;
1357
1358 dp_iresfl = false;
1359 dp_isynfl = false;
1360
1361 }
1362
1363
1364 /////////////////////////////////////////////////////////////////////////////
1365 bool cNoradSDP4::DeepInit(double *eosq, double *sinio, double *cosio,
1366 double *betao, double *aodp, double *theta2,
1367 double *sing, double *cosg, double *betao2,
1368 double *xmdot, double *omgdot, double *xnodott)
1369 {
1370 eqsq = *eosq;
1371 siniq = *sinio;
1372 cosiq = *cosio;
1373 rteqsq = *betao;
1374 ao = *aodp;
1375 cosq2 = *theta2;
1376 sinomo = *sing;
1377 cosomo = *cosg;
1378 bsq = *betao2;
1379 xlldot = *xmdot;
1380 omgdt = *omgdot;
1381 xnodot = *xnodott;
1382
1383 // Deep space initialization
1384 cJulian jd = m_Orbit.Epoch();
1385
1386 dp_thgr = jd.toGMST();
1387
1388 double eq = m_Orbit.Eccentricity();
1389 double aqnv = 1.0 / ao;
1390
1391 dp_xqncl = m_Orbit.Inclination();
1392
1393 double xmao = m_Orbit.mnAnomaly();
1394 double xpidot = omgdt + xnodot;
1395 double sinq = sin(m_Orbit.RAAN());
1396 double cosq = cos(m_Orbit.RAAN());
1397
1398 dp_omegaq = m_Orbit.ArgPerigee();
1399
1400 // Initialize lunar solar terms
1401 double day = jd.FromJan1_12h_1900();
1402
1403 if (day != dpi_day)
1404 {
1405 dpi_day = day;
1406 dpi_xnodce = 4.5236020 - 9.2422029E-4 * day;
1407 dpi_stem = sin(dpi_xnodce);
1408 dpi_ctem = cos(dpi_xnodce);
1409 dpi_zcosil = 0.91375164 - 0.03568096 * dpi_ctem;
1410 dpi_zsinil = sqrt(1.0 - dpi_zcosil * dpi_zcosil);
1411 dpi_zsinhl = 0.089683511 *dpi_stem / dpi_zsinil;
1412 dpi_zcoshl = sqrt(1.0 - dpi_zsinhl * dpi_zsinhl);
1413 dpi_c = 4.7199672 + 0.22997150 * day;
1414 dpi_gam = 5.8351514 + 0.0019443680 * day;
1415 dp_zmol = Fmod2p(dpi_c - dpi_gam);
1416 dpi_zx = 0.39785416 * dpi_stem / dpi_zsinil;
1417 dpi_zy = dpi_zcoshl * dpi_ctem + 0.91744867 * dpi_zsinhl * dpi_stem;
1418 dpi_zx = AcTan(dpi_zx,dpi_zy) + dpi_gam - dpi_xnodce;
1419 dpi_zcosgl = cos(dpi_zx);
1420 dpi_zsingl = sin(dpi_zx);
1421 dp_zmos = 6.2565837 + 0.017201977 * day;
1422 dp_zmos = Fmod2p(dp_zmos);
1423 }
1424
1425 dp_savtsn = 1.0e20;
1426
1427 double zcosg = zcosgs;
1428 double zsing = zsings;
1429 double zcosi = zcosis;
1430 double zsini = zsinis;
1431 double zcosh = cosq;
1432 double zsinh = sinq;
1433 double cc = c1ss;
1434 double zn = zns;
1435 double ze = zes;
1436 double zmo = dp_zmos;
1437 double xnoi = 1.0 / m_xnodp;
1438
1439 double a1; double a3; double a7; double a8; double a9; double a10;
1440 double a2; double a4; double a5; double a6; double x1; double x2;
1441 double x3; double x4; double x5; double x6; double x7; double x8;
1442 double z31; double z32; double z33; double z1; double z2; double z3;
1443 double z11; double z12; double z13; double z21; double z22; double z23;
1444 double s3; double s2; double s4; double s1; double s5; double s6;
1445 double s7;
1446 double se = 0.0; double si = 0.0; double sl = 0.0;
1447 double sgh = 0.0; double sh = 0.0;
1448
1449 // Apply the solar and lunar terms on the first pass, then re-apply the
1450 // solar terms again on the second pass.
1451
1452 for (int pass = 1; pass <= 2; pass++)
1453 {
1454 // Do solar terms
1455 a1 = zcosg * zcosh + zsing * zcosi * zsinh;
1456 a3 = -zsing * zcosh + zcosg * zcosi * zsinh;
1457 a7 = -zcosg * zsinh + zsing * zcosi * zcosh;
1458 a8 = zsing * zsini;
1459 a9 = zsing * zsinh + zcosg * zcosi * zcosh;
1460 a10 = zcosg * zsini;
1461 a2 = cosiq * a7 + siniq * a8;
1462 a4 = cosiq * a9 + siniq * a10;
1463 a5 = -siniq * a7 + cosiq * a8;
1464 a6 = -siniq * a9 + cosiq * a10;
1465 x1 = a1 * cosomo + a2 * sinomo;
1466 x2 = a3 * cosomo + a4 * sinomo;
1467 x3 = -a1 * sinomo + a2 * cosomo;
1468 x4 = -a3 * sinomo + a4 * cosomo;
1469 x5 = a5 * sinomo;
1470 x6 = a6 * sinomo;
1471 x7 = a5 * cosomo;
1472 x8 = a6 * cosomo;
1473 z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3;
1474 z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4;
1475 z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4;
1476 z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * eqsq;
1477 z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * eqsq;
1478 z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * eqsq;
1479 z11 = -6.0 * a1 * a5 + eqsq*(-24.0 * x1 * x7 - 6.0 * x3 * x5);
1480 z12 = -6.0 * (a1 * a6 + a3 * a5) +
1481 eqsq * (-24.0 * (x2 * x7 + x1 * x8) - 6.0 * (x3 * x6 + x4 * x5));
1482 z13 = -6.0 * a3 * a6 + eqsq * (-24.0 * x2 * x8 - 6.0 * x4 * x6);
1483 z21 = 6.0 * a2 * a5 + eqsq * (24.0 * x1 * x5 - 6.0 * x3 * x7);
1484 z22 = 6.0*(a4 * a5 + a2 * a6) +
1485 eqsq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8));
1486 z23 = 6.0 * a4 * a6 + eqsq*(24.0 * x2 * x6 - 6.0 * x4 * x8);
1487 z1 = z1 + z1 + bsq * z31;
1488 z2 = z2 + z2 + bsq * z32;
1489 z3 = z3 + z3 + bsq * z33;
1490 s3 = cc * xnoi;
1491 s2 = -0.5 * s3/rteqsq;
1492 s4 = s3 * rteqsq;
1493 s1 = -15.0 * eq * s4;
1494 s5 = x1 * x3 + x2 * x4;
1495 s6 = x2 * x3 + x1 * x4;
1496 s7 = x2 * x4 - x1 * x3;
1497 se = s1 * zn * s5;
1498 si = s2 * zn * (z11 + z13);
1499 sl = -zn * s3 * (z1 + z3 - 14.0 - 6.0 * eqsq);
1500 sgh = s4 * zn * (z31 + z33 - 6.0);
1501 sh = -zn * s2 * (z21 + z23);
1502
1503 if (dp_xqncl < 5.2359877E-2)
1504 sh = 0.0;
1505
1506 dp_ee2 = 2.0 * s1 * s6;
1507 dp_e3 = 2.0 * s1 * s7;
1508 dp_xi2 = 2.0 * s2 * z12;
1509 dp_xi3 = 2.0 * s2 * (z13 - z11);
1510 dp_xl2 = -2.0 * s3 * z2;
1511 dp_xl3 = -2.0 * s3 * (z3 - z1);
1512 dp_xl4 = -2.0 * s3 * (-21.0 - 9.0 * eqsq) * ze;
1513 dp_xgh2 = 2.0 * s4 * z32;
1514 dp_xgh3 = 2.0 * s4 * (z33 - z31);
1515 dp_xgh4 = -18.0 * s4 * ze;
1516 dp_xh2 = -2.0 * s2 * z22;
1517 dp_xh3 = -2.0 * s2 * (z23 - z21);
1518
1519 if (pass == 1)
1520 {
1521 // Do lunar terms
1522 dp_sse = se;
1523 dp_ssi = si;
1524 dp_ssl = sl;
1525 dp_ssh = sh / siniq;
1526 dp_ssg = sgh - cosiq * dp_ssh;
1527 dp_se2 = dp_ee2;
1528 dp_si2 = dp_xi2;
1529 dp_sl2 = dp_xl2;
1530 dp_sgh2 = dp_xgh2;
1531 dp_sh2 = dp_xh2;
1532 dp_se3 = dp_e3;
1533 dp_si3 = dp_xi3;
1534 dp_sl3 = dp_xl3;
1535 dp_sgh3 = dp_xgh3;
1536 dp_sh3 = dp_xh3;
1537 dp_sl4 = dp_xl4;
1538 dp_sgh4 = dp_xgh4;
1539 zcosg = dpi_zcosgl;
1540 zsing = dpi_zsingl;
1541 zcosi = dpi_zcosil;
1542 zsini = dpi_zsinil;
1543 zcosh = dpi_zcoshl * cosq + dpi_zsinhl * sinq;
1544 zsinh = sinq * dpi_zcoshl - cosq * dpi_zsinhl;
1545 zn = znl;
1546 cc = c1l;
1547 ze = zel;
1548 zmo = dp_zmol;
1549 }
1550 }
1551
1552 dp_sse = dp_sse + se;
1553 dp_ssi = dp_ssi + si;
1554 dp_ssl = dp_ssl + sl;
1555 dp_ssg = dp_ssg + sgh - cosiq / siniq * sh;
1556 dp_ssh = dp_ssh + sh / siniq;
1557
1558 // Geopotential resonance initialization for 12 hour orbits
1559 dp_iresfl = false;
1560 dp_isynfl = false;
1561
1562 bool bInitOnExit = true;
1563 double g310;
1564 double f220;
1565 double bfact = 0.0;
1566
1567 if ((m_xnodp >= 0.0052359877) || (m_xnodp <= 0.0034906585))
1568 {
1569 if ((m_xnodp < 8.26E-3) || (m_xnodp > 9.24E-3) || (eq < 0.5))
1570 {
1571 bInitOnExit = false;
1572 }
1573 else
1574 {
1575 dp_iresfl = true;
1576
1577 double eoc = eq * eqsq;
1578 double g201 = -0.306 - (eq - 0.64) * 0.440;
1579
1580 double g211; double g322;
1581
1582 double g410; double g422;
1583 double g520;
1584
1585 if (eq <= 0.65)
1586 {
1587 g211 = 3.616 - 13.247 * eq + 16.290 * eqsq;
1588 g310 = -19.302 + 117.390 * eq - 228.419 * eqsq + 156.591 * eoc;
1589 g322 = -18.9068 + 109.7927 * eq - 214.6334 * eqsq + 146.5816 * eoc;
1590 g410 = -41.122 + 242.694 * eq - 471.094 * eqsq + 313.953 * eoc;
1591 g422 = -146.407 + 841.880 * eq - 1629.014 * eqsq + 1083.435 * eoc;
1592 g520 = -532.114 + 3017.977 * eq - 5740.0 * eqsq + 3708.276 * eoc;
1593 }
1594 else
1595 {
1596 g211 = -72.099 + 331.819 * eq - 508.738 * eqsq + 266.724 * eoc;
1597 g310 = -346.844 + 1582.851 * eq - 2415.925 * eqsq + 1246.113 * eoc;
1598 g322 = -342.585 + 1554.908 * eq - 2366.899 * eqsq + 1215.972 * eoc;
1599 g410 = -1052.797 + 4758.686 * eq - 7193.992 * eqsq + 3651.957 * eoc;
1600 g422 = -3581.69 + 16178.11 * eq - 24462.77 * eqsq + 12422.52 * eoc;
1601
1602 if (eq <= 0.715)
1603 g520 = 1464.74 - 4664.75 * eq + 3763.64 * eqsq;
1604 else
1605 g520 = -5149.66 + 29936.92 * eq - 54087.36 * eqsq + 31324.56 * eoc;
1606 }
1607
1608 double g533;
1609 double g521;
1610 double g532;
1611
1612 if (eq < 0.7)
1613 {
1614 g533 = -919.2277 + 4988.61 * eq - 9064.77 * eqsq + 5542.21 * eoc;
1615 g521 = -822.71072 + 4568.6173 * eq - 8491.4146 * eqsq + 5337.524 * eoc;
1616 g532 = -853.666 + 4690.25 * eq - 8624.77 * eqsq + 5341.4 * eoc;
1617 }
1618 else
1619 {
1620 g533 = -37995.78 + 161616.52 * eq - 229838.2 * eqsq + 109377.94 * eoc;
1621 g521 = -51752.104 + 218913.95 * eq - 309468.16 * eqsq + 146349.42 * eoc;
1622 g532 = -40023.88 + 170470.89 * eq - 242699.48 * eqsq + 115605.82 * eoc;
1623 }
1624
1625 double sini2 = siniq * siniq;
1626 f220 = 0.75*(1.0 + 2.0 * cosiq + cosq2);
1627 double f221 = 1.5 * sini2;
1628 double f321 = 1.875 * siniq*(1.0 - 2.0 * cosiq - 3.0 * cosq2);
1629 double f322 = -1.875 * siniq*(1.0 + 2.0 * cosiq - 3.0 * cosq2);
1630 double f441 = 35.0 * sini2 * f220;
1631 double f442 = 39.3750 * sini2 * sini2;
1632 double f522 = 9.84375 * siniq*(sini2*(1.0 - 2.0 * cosiq - 5.0 * cosq2) +
1633 0.33333333*(-2.0 + 4.0 * cosiq + 6.0 * cosq2));
1634 double f523 = siniq*(4.92187512 * sini2*(-2.0 - 4.0 * cosiq + 10.0 * cosq2) +
1635 6.56250012 * (1.0 + 2.0 * cosiq - 3.0 * cosq2));
1636 double f542 = 29.53125 * siniq*(2.0 - 8.0 * cosiq + cosq2 * (-12.0 + 8.0 * cosiq + 10.0 * cosq2));
1637 double f543 = 29.53125 * siniq*(-2.0 - 8.0 * cosiq + cosq2 * (12.0 + 8.0 * cosiq - 10.0 * cosq2));
1638 double xno2 = m_xnodp * m_xnodp;
1639 double ainv2 = aqnv * aqnv;
1640 double temp1 = 3.0 * xno2 * ainv2;
1641 double temp = temp1 * root22;
1642
1643 dp_d2201 = temp * f220 * g201;
1644 dp_d2211 = temp * f221 * g211;
1645 temp1 = temp1 * aqnv;
1646 temp = temp1 * root32;
1647 dp_d3210 = temp * f321 * g310;
1648 dp_d3222 = temp * f322 * g322;
1649 temp1 = temp1 * aqnv;
1650 temp = 2.0 * temp1 * root44;
1651 dp_d4410 = temp * f441 * g410;
1652 dp_d4422 = temp * f442 * g422;
1653 temp1 = temp1 * aqnv;
1654 temp = temp1 * root52;
1655 dp_d5220 = temp * f522 * g520;
1656 dp_d5232 = temp * f523 * g532;
1657 temp = 2.0 * temp1 * root54;
1658 dp_d5421 = temp * f542 * g521;
1659 dp_d5433 = temp * f543 * g533;
1660 dp_xlamo = xmao + m_Orbit.RAAN() + m_Orbit.RAAN() - dp_thgr - dp_thgr;
1661 bfact = xlldot + xnodot + xnodot - thdt - thdt;
1662 bfact = bfact + dp_ssl + dp_ssh + dp_ssh;
1663 }
1664 }
1665 else
1666 {
1667 // Synchronous resonance terms initialization
1668 dp_iresfl = true;
1669 dp_isynfl = true;
1670 double g200 = 1.0 + eqsq * (-2.5 + 0.8125 * eqsq);
1671 g310 = 1.0 + 2.0 * eqsq;
1672 double g300 = 1.0 + eqsq * (-6.0 + 6.60937 * eqsq);
1673 f220 = 0.75 * (1.0 + cosiq) * (1.0 + cosiq);
1674 double f311 = 0.9375 * siniq * siniq * (1.0 + 3 * cosiq) - 0.75 * (1.0 + cosiq);
1675 double f330 = 1.0 + cosiq;
1676 f330 = 1.875 * f330 * f330 * f330;
1677 dp_del1 = 3.0 * m_xnodp * m_xnodp * aqnv * aqnv;
1678 dp_del2 = 2.0 * dp_del1 * f220 * g200 * q22;
1679 dp_del3 = 3.0 * dp_del1 * f330 * g300 * q33 * aqnv;
1680 dp_del1 = dp_del1 * f311 * g310 * q31 * aqnv;
1681 dp_fasx2 = 0.13130908;
1682 dp_fasx4 = 2.8843198;
1683 dp_fasx6 = 0.37448087;
1684 dp_xlamo = xmao + m_Orbit.RAAN() + m_Orbit.ArgPerigee() - dp_thgr;
1685 bfact = xlldot + xpidot - thdt;
1686 bfact = bfact + dp_ssl + dp_ssg + dp_ssh;
1687 }
1688
1689 if (bInitOnExit)
1690 {
1691 dp_xfact = bfact - m_xnodp;
1692
1693 // Initialize integrator
1694 dp_xli = dp_xlamo;
1695 dp_xni = m_xnodp;
1696 dp_atime = 0.0;
1697 dp_stepp = 720.0;
1698 dp_stepn = -720.0;
1699 dp_step2 = 259200.0;
1700 }
1701
1702 *eosq = eqsq;
1703 *sinio = siniq;
1704 *cosio = cosiq;
1705 *betao = rteqsq;
1706 *aodp = ao;
1707 *theta2 = cosq2;
1708 *sing = sinomo;
1709 *cosg = cosomo;
1710 *betao2 = bsq;
1711 *xmdot = xlldot;
1712 *omgdot = omgdt;
1713 *xnodott = xnodot;
1714
1715 return true;
1716 }
1717
1718 //////////////////////////////////////////////////////////////////////////////
1719 bool cNoradSDP4::DeepCalcDotTerms(double *pxndot, double *pxnddt, double *pxldot)
1720 {
1721 // Dot terms calculated
1722 if (dp_isynfl)
1723 {
1724 *pxndot = dp_del1 * sin(dp_xli - dp_fasx2) +
1725 dp_del2 * sin(2.0 * (dp_xli - dp_fasx4)) +
1726 dp_del3 * sin(3.0 * (dp_xli - dp_fasx6));
1727 *pxnddt = dp_del1 * cos(dp_xli - dp_fasx2) +
1728 2.0 * dp_del2 * cos(2.0 * (dp_xli - dp_fasx4)) +
1729 3.0 * dp_del3 * cos(3.0 * (dp_xli - dp_fasx6));
1730 }
1731 else
1732 {
1733 double xomi = dp_omegaq + omgdt * dp_atime;
1734 double x2omi = xomi + xomi;
1735 double x2li = dp_xli + dp_xli;
1736
1737 *pxndot = dp_d2201 * sin(x2omi + dp_xli - g22) +
1738 dp_d2211 * sin(dp_xli - g22) +
1739 dp_d3210 * sin(xomi + dp_xli - g32) +
1740 dp_d3222 * sin(-xomi + dp_xli - g32) +
1741 dp_d4410 * sin(x2omi + x2li - g44) +
1742 dp_d4422 * sin(x2li - g44) +
1743 dp_d5220 * sin(xomi + dp_xli - g52) +
1744 dp_d5232 * sin(-xomi + dp_xli - g52) +
1745 dp_d5421 * sin(xomi + x2li - g54) +
1746 dp_d5433 * sin(-xomi + x2li - g54);
1747
1748 *pxnddt = dp_d2201 * cos(x2omi + dp_xli - g22) +
1749 dp_d2211 * cos(dp_xli - g22) +
1750 dp_d3210 * cos(xomi + dp_xli - g32) +
1751 dp_d3222 * cos(-xomi + dp_xli - g32) +
1752 dp_d5220 * cos(xomi + dp_xli - g52) +
1753 dp_d5232 * cos(-xomi + dp_xli - g52) +
1754 2.0 * (dp_d4410 * cos(x2omi + x2li - g44) +
1755 dp_d4422 * cos(x2li - g44) +
1756 dp_d5421 * cos(xomi + x2li - g54) +
1757 dp_d5433 * cos(-xomi + x2li - g54));
1758 }
1759
1760 *pxldot = dp_xni + dp_xfact;
1761 *pxnddt = (*pxnddt) * (*pxldot);
1762
1763 return true;
1764 }
1765
1766 //////////////////////////////////////////////////////////////////////////////
1767 void cNoradSDP4::DeepCalcIntegrator(double *pxndot, double *pxnddt,
1768 double *pxldot, const double &delt)
1769 {
1770 DeepCalcDotTerms(pxndot, pxnddt, pxldot);
1771
1772 dp_xli = dp_xli + (*pxldot) * delt + (*pxndot) * dp_step2;
1773 dp_xni = dp_xni + (*pxndot) * delt + (*pxnddt) * dp_step2;
1774 dp_atime = dp_atime + delt;
1775 }
1776
1777 //////////////////////////////////////////////////////////////////////////////
1778 bool cNoradSDP4::DeepSecular(double *xmdf, double *omgadf, double *xnode,
1779 double *emm, double *xincc, double *xnn,
1780 double *tsince)
1781 {
1782 xll = *xmdf;
1783 omgasm = *omgadf;
1784 xnodes = *xnode;
1785 xn = *xnn;
1786 t = *tsince;
1787
1788 // Deep space secular effects
1789 xll = xll + dp_ssl * t;
1790 omgasm = omgasm + dp_ssg * t;
1791 xnodes = xnodes + dp_ssh * t;
1792 _em = m_Orbit.Eccentricity() + dp_sse * t;
1793 xinc = m_Orbit.Inclination() + dp_ssi * t;
1794
1795 if (xinc < 0.0)
1796 {
1797 xinc = -xinc;
1798 xnodes = xnodes + PI;
1799 omgasm = omgasm - PI;
1800 }
1801
1802 double xnddt = 0.0;
1803 double xndot = 0.0;
1804 double xldot = 0.0;
1805 double ft = 0.0;
1806 double delt = 0.0;
1807
1808 bool fDone = false;
1809
1810 if (dp_iresfl)
1811 {
1812 while (!fDone)
1813 {
1814 if ((dp_atime == 0.0) ||
1815 ((t >= 0.0) && (dp_atime < 0.0)) ||
1816 ((t < 0.0) && (dp_atime >= 0.0)))
1817 {
1818 if (t < 0)
1819 delt = dp_stepn;
1820 else
1821 delt = dp_stepp;
1822
1823 // Epoch restart
1824 dp_atime = 0.0;
1825 dp_xni = m_xnodp;
1826 dp_xli = dp_xlamo;
1827
1828 fDone = true;
1829 }
1830 else
1831 {
1832 if (fabs(t) < fabs(dp_atime))
1833 {
1834 delt = dp_stepp;
1835
1836 if (t >= 0.0)
1837 delt = dp_stepn;
1838
1839 DeepCalcIntegrator(&xndot, &xnddt, &xldot, delt);
1840 }
1841 else
1842 {
1843 delt = dp_stepn;
1844
1845 delt = dp_stepp;
1846
1847 fDone = true;
1848 }
1849 }
1850 }
1851
1852 while (fabs(t - dp_atime) >= dp_stepp)
1853 {
1854 DeepCalcIntegrator(&xndot, &xnddt, &xldot, delt);
1855 }
1856
1857 ft = t - dp_atime;
1858
1859 DeepCalcDotTerms(&xndot, &xnddt, &xldot);
1860
1861 xn = dp_xni + xndot * ft + xnddt * ft * ft * 0.5;
1862
1863 double xl = dp_xli + xldot * ft + xndot * ft * ft * 0.5;
1864 double temp = -xnodes + dp_thgr + t * thdt;
1865
1866 xll = xl - omgasm + temp;
1867
1868 if (!dp_isynfl)
1869 xll = xl + temp + temp;
1870 }
1871
1872 *xmdf = xll;
1873 *omgadf = omgasm;
1874 *xnode = xnodes;
1875 *emm = _em;
1876 *xincc = xinc;
1877 *xnn = xn;
1878 *tsince = t;
1879
1880 return true;
1881 }
1882
1883 //////////////////////////////////////////////////////////////////////////////
1884 bool cNoradSDP4::DeepPeriodics(double *e, double *xincc,
1885 double *omgadf, double *xnode,
1886 double *xmam)
1887 {
1888 _em = *e;
1889 xinc = *xincc;
1890 omgasm = *omgadf;
1891 xnodes = *xnode;
1892 xll = *xmam;
1893
1894 // Lunar-solar periodics
1895 double sinis = sin(xinc);
1896 double cosis = cos(xinc);
1897
1898 double sghs = 0.0;
1899 double shs = 0.0;
1900 double sh1 = 0.0;
1901 double pe = 0.0;
1902 double pinc = 0.0;
1903 double pl = 0.0;
1904 double sghl = 0.0;
1905
1906 if (fabs(dp_savtsn - t) >= 30.0)
1907 {
1908 dp_savtsn = t;
1909
1910 double zm = dp_zmos + zns * t;
1911 double zf = zm + 2.0 * zes * sin(zm);
1912 double sinzf = sin(zf);
1913 double f2 = 0.5 * sinzf * sinzf - 0.25;
1914 double f3 = -0.5 * sinzf * cos(zf);
1915 double ses = dp_se2 * f2 + dp_se3 * f3;
1916 double sis = dp_si2 * f2 + dp_si3 * f3;
1917 double sls = dp_sl2 * f2 + dp_sl3 * f3 + dp_sl4 * sinzf;
1918
1919 sghs = dp_sgh2 * f2 + dp_sgh3 * f3 + dp_sgh4 * sinzf;
1920 shs = dp_sh2 * f2 + dp_sh3 * f3;
1921 zm = dp_zmol + znl * t;
1922 zf = zm + 2.0 * zel * sin(zm);
1923 sinzf = sin(zf);
1924 f2 = 0.5 * sinzf * sinzf - 0.25;
1925 f3 = -0.5 * sinzf * cos(zf);
1926
1927 double sel = dp_ee2 * f2 + dp_e3 * f3;
1928 double sil = dp_xi2 * f2 + dp_xi3 * f3;
1929 double sll = dp_xl2 * f2 + dp_xl3 * f3 + dp_xl4 * sinzf;
1930
1931 sghl = dp_xgh2 * f2 + dp_xgh3 * f3 + dp_xgh4 * sinzf;
1932 sh1 = dp_xh2 * f2 + dp_xh3 * f3;
1933 pe = ses + sel;
1934 pinc = sis + sil;
1935 pl = sls + sll;
1936 }
1937
1938 double pgh = sghs + sghl;
1939 double ph = shs + sh1;
1940 xinc = xinc + pinc;
1941 _em = _em + pe;
1942
1943 if (dp_xqncl >= 0.2)
1944 {
1945 // Apply periodics directly
1946 ph = ph / siniq;
1947 pgh = pgh - cosiq * ph;
1948 omgasm = omgasm + pgh;
1949 xnodes = xnodes + ph;
1950 xll = xll + pl;
1951 }
1952 else
1953 {
1954 // Apply periodics with Lyddane modification
1955 double sinok = sin(xnodes);
1956 double cosok = cos(xnodes);
1957 double alfdp = sinis * sinok;
1958 double betdp = sinis * cosok;
1959 double dalf = ph * cosok + pinc * cosis * sinok;
1960 double dbet = -ph * sinok + pinc * cosis * cosok;
1961
1962 alfdp = alfdp + dalf;
1963 betdp = betdp + dbet;
1964
1965 double xls = xll + omgasm + cosis * xnodes;
1966 double dls = pl + pgh - pinc * xnodes * sinis;
1967
1968 xls = xls + dls;
1969 xnodes = AcTan(alfdp, betdp);
1970 xll = xll + pl;
1971 omgasm = xls - xll - cos(xinc) * xnodes;
1972 }
1973
1974 *e = _em;
1975 *xincc = xinc;
1976 *omgadf = omgasm;
1977
1978 *xnode = xnodes;
1979 *xmam = xll;
1980
1981 return true;
1982 }
1983
1984 //////////////////////////////////////////////////////////////////////////////
1985 // getPosition()
1986 // This procedure returns the ECI position and velocity for the satellite
1987 // in the orbit at the given number of minutes since the TLE epoch time
1988 // using the NORAD Simplified General Perturbation 4, "deep space" orbit
1989 // model.
1990 //
1991 // tsince - Time in minutes since the TLE epoch (GMT).
1992 // pECI - pointer to location to store the ECI data.
1993 // To convert the returned ECI position vector to km,
1994 // multiply each component by:
1995 // (XKMPER_WGS72 / AE).
1996 // To convert the returned ECI velocity vector to km/sec,
1997 // multiply each component by:
1998 // (XKMPER_WGS72 / AE) * (MIN_PER_DAY / 86400).
1999 bool cNoradSDP4::getPosition(double tsince, cEci &eci)
2000 {
2001 DeepInit(&m_eosq, &m_sinio, &m_cosio, &m_betao, &m_aodp, &m_theta2,
2002 &m_sing, &m_cosg, &m_betao2, &m_xmdot, &m_omgdot, &m_xnodot);
2003
2004 // Update for secular gravity and atmospheric drag
2005 double xmdf = m_Orbit.mnAnomaly() + m_xmdot * tsince;
2006 double omgadf = m_Orbit.ArgPerigee() + m_omgdot * tsince;
2007 double xnoddf = m_Orbit.RAAN() + m_xnodot * tsince;
2008 double tsq = tsince * tsince;
2009 double xnode = xnoddf + m_xnodcf * tsq;
2010 double tempa = 1.0 - m_c1 * tsince;
2011 double tempe = m_Orbit.BStar() * m_c4 * tsince;
2012 double templ = m_t2cof * tsq;
2013 double xn = m_xnodp;
2014 double em;
2015 double xinc;
2016
2017 DeepSecular(&xmdf, &omgadf, &xnode, &em, &xinc, &xn, &tsince);
2018
2019 double a = pow(XKE / xn, TWOTHRD) * sqr(tempa);
2020 double e = em - tempe;
2021 double xmam = xmdf + m_xnodp * templ;
2022
2023 DeepPeriodics(&e, &xinc, &omgadf, &xnode, &xmam);
2024
2025 double xl = xmam + omgadf + xnode;
2026
2027 xn = XKE / pow(a, 1.5);
2028
2029 return FinalPosition(xinc, omgadf, e, a, xl, xnode, xn, tsince, eci);
2030 }
2031
2032
2033 // cOrbit.cpp
2034 //
2035 // Copyright (c) 2002-2003 Michael F. Henry
2036 //
2037 // mfh 11/15/2003
2038 //
2039 //////////////////////////////////////////////////////////////////////
2040 cOrbit::cOrbit(const cTle &tle) :
2041 m_tle(tle),
2042 m_pNoradModel(NULL)
2043 {
2044 m_tle.Initialize();
2045
2046 int epochYear = (int)m_tle.getField(cTle::FLD_EPOCHYEAR);
2047 double epochDay = m_tle.getField(cTle::FLD_EPOCHDAY );
2048
2049 if (epochYear < 57)
2050 epochYear += 2000;
2051 else
2052 epochYear += 1900;
2053
2054 m_jdEpoch = cJulian(epochYear, epochDay);
2055
2056 m_secPeriod = -1.0;
2057
2058 // Recover the original mean motion and semimajor axis from the
2059 // input elements.
2060 double mm = mnMotion();
2061 double rpmin = mm * 2 * PI / MIN_PER_DAY; // rads per minute
2062
2063 double a1 = pow(XKE / rpmin, TWOTHRD);
2064 double e = Eccentricity();
2065 double i = Inclination();
2066 double temp = (1.5 * CK2 * (3.0 * sqr(cos(i)) - 1.0) /
2067 pow(1.0 - e * e, 1.5));
2068 double delta1 = temp / (a1 * a1);
2069 double a0 = a1 *
2070 (1.0 - delta1 *
2071 ((1.0 / 3.0) + delta1 *
2072 (1.0 + 134.0 / 81.0 * delta1)));
2073
2074 double delta0 = temp / (a0 * a0);
2075
2076 m_mnMotionRec = rpmin / (1.0 + delta0);
2077 m_aeAxisSemiMinorRec = a0 / (1.0 - delta0);
2078 m_aeAxisSemiMajorRec = m_aeAxisSemiMinorRec / sqrt(1.0 - (e * e));
2079 m_kmPerigeeRec = XKMPER_WGS72 * (m_aeAxisSemiMajorRec * (1.0 - e) - AE);
2080 m_kmApogeeRec = XKMPER_WGS72 * (m_aeAxisSemiMajorRec * (1.0 + e) - AE);
2081
2082 if (2.0 * PI / m_mnMotionRec >= 225.0)
2083 {
2084 // SDP4 - period >= 225 minutes.
2085 m_pNoradModel = new cNoradSDP4(*this);
2086 }
2087 else
2088 {
2089 // SGP4 - period < 225 minutes
2090 m_pNoradModel = new cNoradSGP4(*this);
2091 }
2092 }
2093
2094 /////////////////////////////////////////////////////////////////////////////
2095 cOrbit::~cOrbit()
2096 {
2097 delete m_pNoradModel;
2098 }
2099
2100 //////////////////////////////////////////////////////////////////////////////
2101 // Return the period in seconds
2102 double cOrbit::Period() const
2103 {
2104 if (m_secPeriod < 0.0)
2105 {
2106 // Calculate the period using the recovered mean motion.
2107 if (m_mnMotionRec == 0)
2108 m_secPeriod = 0.0;
2109 else
2110 m_secPeriod = (2 * PI) / m_mnMotionRec * 60.0;
2111 }
2112
2113 return m_secPeriod;
2114 }
2115
2116 //////////////////////////////////////////////////////////////////////////////
2117 // Returns elapsed number of seconds from epoch to given time.
2118 // Note: "Predicted" TLEs can have epochs in the future.
2119 double cOrbit::TPlusEpoch(const cJulian &gmt) const
2120 {
2121 return gmt.spanSec(Epoch());
2122 }
2123
2124 //////////////////////////////////////////////////////////////////////////////
2125 // Returns the mean anomaly in radians at given GMT.
2126 // At epoch, the mean anomaly is given by the elements data.
2127 double cOrbit::mnAnomaly(cJulian gmt) const
2128 {
2129 double span = TPlusEpoch(gmt);
2130 double P = Period();
2131
2132 assert(P != 0.0);
2133
2134 return fmod(mnAnomaly() + (TWOPI * (span / P)), TWOPI);
2135 }
2136
2137 //////////////////////////////////////////////////////////////////////////////
2138 // getPosition()
2139 // This procedure returns the ECI position and velocity for the satellite
2140 // at "tsince" minutes from the (GMT) TLE epoch. The vectors returned in
2141 // the ECI object are kilometer-based.
2142 // tsince - Time in minutes since the TLE epoch (GMT).
2143 bool cOrbit::getPosition(double tsince, cEci *pEci) const
2144 {
2145 bool rc;
2146
2147 rc = m_pNoradModel->getPosition(tsince, *pEci);
2148
2149 pEci->ae2km();
2150
2151 return rc;
2152 }
2153
2154 //////////////////////////////////////////////////////////////////////////////
2155 // SatName()
2156 // Return the name of the satellite. If requested, the NORAD number is
2157 // appended to the end of the name, i.e., "ISS (ZARYA) #25544".
2158 // The name of the satellite with the NORAD number appended is important
2159 // because many satellites, especially debris, have the same name and
2160 // would otherwise appear to be the same satellite in ouput data.
2161 string cOrbit::SatName(bool fAppendId /* = false */) const
2162 {
2163 string str = m_tle.getName();
2164
2165 if (fAppendId)
2166 {
2167 string strId;
2168
2169 m_tle.getField(cTle::FLD_NORADNUM, cTle::U_NATIVE, &strId);
2170 str = str + " #" + strId;
2171 }
2172
2173 return str;
2174 }
2175

  ViewVC Help
Powered by ViewVC 1.1.23