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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations) (download)
Wed Mar 6 10:45:49 2013 UTC (11 years, 8 months ago) by mocchiut
Branch: MAIN
CVS Tags: v10RED
Changes since 1.5: +1 -1 lines
TLE 366.8 day bug fixed

1 mocchiut 1.1 //
2     // globals.cpp
3     //
4     #include <sgp4.h>
5 pam-fi 1.3 #include <cstring>
6 mocchiut 1.1
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 mocchiut 1.6 assert((day >= 0.0) && (day <= 366.81)); // 366.5 // 366.7
235 mocchiut 1.1
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 mocchiut 1.4 // double zmo = dp_zmos;
1437 mocchiut 1.1 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 mocchiut 1.4 // zmo = dp_zmol;
1549 mocchiut 1.1 }
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