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

Contents of /YodaProfiler/src/sgp4.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.1 - (show annotations) (download)
Fri Oct 20 11:39:40 2006 UTC (18 years, 1 month ago) by mocchiut
Branch: MAIN
CVS Tags: v2r06, v2r05, v2r04, v2r01, v2r00, v2r03, v2r02
Create libsgp4.so shared lib; unified sgp4 code

1 //
2 // globals.cpp
3 //
4 #include <sgp4.h>
5
6 //////////////////////////////////////////////////////////////////////////////
7 double sqr(const double x)
8 {
9 return (x * x);
10 }
11
12 //////////////////////////////////////////////////////////////////////////////
13 double Fmod2p(const double arg)
14 {
15 double modu = fmod(arg, TWOPI);
16
17 if (modu < 0.0)
18 modu += TWOPI;
19
20 return modu;
21 }
22
23 //////////////////////////////////////////////////////////////////////////////
24 // AcTan()
25 // ArcTangent of sin(x) / cos(x). The advantage of this function over arctan()
26 // is that it returns the correct quadrant of the angle.
27 double AcTan(const double sinx, const double cosx)
28 {
29 double ret;
30
31 if (cosx == 0.0)
32 {
33 if (sinx > 0.0)
34 ret = PI / 2.0;
35 else
36 ret = 3.0 * PI / 2.0;
37 }
38 else
39 {
40 if (cosx > 0.0)
41 ret = atan(sinx / cosx);
42 else
43 ret = PI + atan(sinx / cosx);
44 }
45
46 return ret;
47 }
48
49 //////////////////////////////////////////////////////////////////////////////
50 double rad2deg(const double r)
51 {
52 const double DEG_PER_RAD = 180.0 / PI;
53 return r * DEG_PER_RAD;
54 }
55
56 //////////////////////////////////////////////////////////////////////////////
57 double deg2rad(const double d)
58 {
59 const double RAD_PER_DEG = PI / 180.0;
60 return d * RAD_PER_DEG;
61 }
62
63 //
64 // coord.cpp
65 //
66 // Copyright (c) 2003 Michael F. Henry
67 //
68
69 //////////////////////////////////////////////////////////////////////
70 // cCoordGeo Class
71 //////////////////////////////////////////////////////////////////////
72
73 cCoordGeo::cCoordGeo()
74 {
75 m_Lat = 0.0;
76 m_Lon = 0.0;
77 m_Alt = 0.0;
78 }
79
80 //////////////////////////////////////////////////////////////////////
81 // cCoordTopo Class
82 //////////////////////////////////////////////////////////////////////
83
84 cCoordTopo::cCoordTopo()
85 {
86 m_Az = 0.0;
87 m_El = 0.0;
88 m_Range = 0.0;
89 m_RangeRate = 0.0;
90
91 }
92
93
94
95 //
96 // cVector.cpp
97 //
98 // Copyright (c) 2001-2003 Michael F. Henry
99 //
100 //*****************************************************************************
101 // Multiply each component in the vector by 'factor'.
102 //*****************************************************************************
103 void cVector::Mul(double factor)
104 {
105 m_x *= factor;
106 m_y *= factor;
107 m_z *= factor;
108 m_w *= fabs(factor);
109 }
110
111 //*****************************************************************************
112 // Subtract a vector from this one.
113 //*****************************************************************************
114 void cVector::Sub(const cVector& vec)
115 {
116 m_x -= vec.m_x;
117 m_y -= vec.m_y;
118 m_z -= vec.m_z;
119 m_w -= vec.m_w;
120 }
121
122 //*****************************************************************************
123 // Calculate the angle between this vector and another
124 //*****************************************************************************
125 double cVector::Angle(const cVector& vec) const
126 {
127 return acos(Dot(vec) / (Magnitude() * vec.Magnitude()));
128 }
129
130 //*****************************************************************************
131 //
132 //*****************************************************************************
133 double cVector::Magnitude() const
134 {
135 return sqrt((m_x * m_x) +
136 (m_y * m_y) +
137 (m_z * m_z));
138 }
139
140 //*****************************************************************************
141 // Return the dot product
142 //*****************************************************************************
143 double cVector::Dot(const cVector& vec) const
144 {
145 return (m_x * vec.m_x) +
146 (m_y * vec.m_y) +
147 (m_z * vec.m_z);
148 }
149 //
150 // cJulian.cpp
151 //
152 // This class encapsulates Julian dates with the epoch of 12:00 noon (12:00 UT)
153 // on January 1, 4713 B.C. Some epoch dates:
154 // 01/01/1990 00:00 UTC - 2447892.5
155 // 01/01/1990 12:00 UTC - 2447893.0
156 // 01/01/2000 00:00 UTC - 2451544.5
157 // 01/01/2001 00:00 UTC - 2451910.5
158 //
159 // Note the Julian day begins at noon, which allows astronomers to have all
160 // the dates in a single observing session the same.
161 //
162 // References:
163 // "Astronomical Formulae for Calculators", Jean Meeus
164 // "Satellite Communications", Dennis Roddy, 2nd Edition, 1995.
165 //
166 // Copyright (c) 2003 Michael F. Henry
167 //
168 // mfh 12/24/2003
169 //
170
171 //////////////////////////////////////////////////////////////////////////////
172 // Create a Julian date object from a time_t object. time_t objects store the
173 // number of seconds since midnight UTC January 1, 1970.
174 cJulian::cJulian(time_t time)
175 {
176 struct tm *ptm = gmtime(&time);
177 assert(ptm);
178
179 int year = ptm->tm_year + 1900;
180 double day = ptm->tm_yday + 1 +
181 (ptm->tm_hour +
182 ((ptm->tm_min +
183 (ptm->tm_sec / 60.0)) / 60.0)) / 24.0;
184
185 Initialize(year, day);
186 }
187
188 //////////////////////////////////////////////////////////////////////////////
189 // Create a Julian date object from a year and day of year.
190 // Example parameters: year = 2001, day = 1.5 (Jan 1 12h)
191 cJulian::cJulian(int year, double day)
192 {
193 Initialize(year, day);
194 }
195
196 //////////////////////////////////////////////////////////////////////////////
197 // Create a Julian date object.
198 cJulian::cJulian(int year, // i.e., 2004
199 int mon, // 1..12
200 int day, // 1..31
201 int hour, // 0..23
202 int min, // 0..59
203 double sec /* = 0.0 */) // 0..(59.999999...)
204
205 {
206 // Calculate N, the day of the year (1..366)
207 int N;
208 int F1 = (int)((275.0 * mon) / 9.0);
209 int F2 = (int)((mon + 9.0) / 12.0);
210
211 if (IsLeapYear(year))
212 {
213 // Leap year
214 N = F1 - F2 + day - 30;
215 }
216 else
217 {
218 // Common year
219 N = F1 - (2 * F2) + day - 30;
220 }
221
222 double dblDay = N + (hour + (min + (sec / 60.0)) / 60.0) / 24.0;
223
224 Initialize(year, dblDay);
225 }
226
227 //////////////////////////////////////////////////////////////////////////////
228 void cJulian::Initialize(int year, double day)
229 {
230 // 1582 A.D.: 10 days removed from calendar
231 // 3000 A.D.: Arbitrary error checking limit
232 assert((year > 1582) && (year < 3000));
233 assert((day >= 0.0) && (day <= 366.5));
234
235 // Now calculate Julian date
236
237 year--;
238
239 // Centuries are not leap years unless they divide by 400
240 int A = (year / 100);
241 int B = 2 - A + (A / 4);
242
243 double NewYears = (int)(365.25 * year) +
244 (int)(30.6001 * 14) +
245 1720994.5 + B; // 1720994.5 = Oct 30, year -1
246
247 m_Date = NewYears + day;
248 }
249
250 //////////////////////////////////////////////////////////////////////////////
251 // getComponent()
252 // Return requested components of date.
253 // Year : Includes the century.
254 // Month: 1..12
255 // Day : 1..31 including fractional part
256 void cJulian::getComponent(int *pYear,
257 int *pMon /* = NULL */,
258 double *pDOM /* = NULL */) const
259 {
260 assert(pYear != NULL);
261
262 double jdAdj = getDate() + 0.5;
263 int Z = (int)jdAdj; // integer part
264 double F = jdAdj - Z; // fractional part
265 double alpha = (int)((Z - 1867216.25) / 36524.25);
266 double A = Z + 1 + alpha - (int)(alpha / 4.0);
267 double B = A + 1524.0;
268 int C = (int)((B - 122.1) / 365.25);
269 int D = (int)(C * 365.25);
270 int E = (int)((B - D) / 30.6001);
271
272 double DOM = B - D - (int)(E * 30.6001) + F;
273 int month = (E < 13.5) ? (E - 1) : (E - 13);
274 int year = (month > 2.5) ? (C - 4716) : (C - 4715);
275
276 *pYear = year;
277
278 if (pMon != NULL)
279 *pMon = month;
280
281 if (pDOM != NULL)
282 *pDOM = DOM;
283 }
284
285 //////////////////////////////////////////////////////////////////////////////
286 // toGMST()
287 // Calculate Greenwich Mean Sidereal Time for the Julian date. The return value
288 // is the angle, in radians, measuring eastward from the Vernal Equinox to the
289 // prime meridian. This angle is also referred to as "ThetaG" (Theta GMST).
290 //
291 // References:
292 // The 1992 Astronomical Almanac, page B6.
293 // Explanatory Supplement to the Astronomical Almanac, page 50.
294 // Orbital Coordinate Systems, Part III, Dr. T.S. Kelso, Satellite Times,
295 // Nov/Dec 1995
296 double cJulian::toGMST() const
297 {
298 const double UT = fmod(m_Date + 0.5, 1.0);
299 const double TU = (FromJan1_12h_2000() - UT) / 36525.0;
300
301 double GMST = 24110.54841 + TU *
302 (8640184.812866 + TU * (0.093104 - TU * 6.2e-06));
303
304 GMST = fmod(GMST + SEC_PER_DAY * OMEGA_E * UT, SEC_PER_DAY);
305
306 if (GMST < 0.0)
307 GMST += SEC_PER_DAY; // "wrap" negative modulo value
308
309 return (TWOPI * (GMST / SEC_PER_DAY));
310 }
311
312 //////////////////////////////////////////////////////////////////////////////
313 // toLMST()
314 // Calculate Local Mean Sidereal Time for given longitude (for this date).
315 // The longitude is assumed to be in radians measured west from Greenwich.
316 // The return value is the angle, in radians, measuring eastward from the
317 // Vernal Equinox to the given longitude.
318 double cJulian::toLMST(double lon) const
319 {
320 return fmod(toGMST() + lon, TWOPI);
321 }
322
323 //////////////////////////////////////////////////////////////////////////////
324 // toTime()
325 // Convert to type time_t
326 // Avoid using this function as it discards the fractional seconds of the
327 // time component.
328 time_t cJulian::toTime() const
329 {
330 int nYear;
331 int nMonth;
332 double dblDay;
333
334 getComponent(&nYear, &nMonth, &dblDay);
335
336 // dblDay is the fractional Julian Day (i.e., 29.5577).
337 // Save the whole number day in nDOM and convert dblDay to
338 // the fractional portion of day.
339 int nDOM = (int)dblDay;
340
341 dblDay -= nDOM;
342
343 const int SEC_PER_MIN = 60;
344 const int SEC_PER_HR = 60 * SEC_PER_MIN;
345 const int SEC_PER_DAY = 24 * SEC_PER_HR;
346
347 int secs = (int)((dblDay * SEC_PER_DAY) + 0.5);
348
349 // Create a "struct tm" type.
350 // NOTE:
351 // The "struct tm" type has a 1-second resolution. Any fractional
352 // component of the "seconds" time value is discarded.
353 struct tm tGMT;
354 memset(&tGMT, 0, sizeof(tGMT));
355
356 tGMT.tm_year = nYear - 1900; // 2001 is 101
357 tGMT.tm_mon = nMonth - 1; // January is 0
358 tGMT.tm_mday = nDOM; // First day is 1
359 tGMT.tm_hour = secs / SEC_PER_HR;
360 tGMT.tm_min = (secs % SEC_PER_HR) / SEC_PER_MIN;
361 tGMT.tm_sec = (secs % SEC_PER_HR) % SEC_PER_MIN;
362 tGMT.tm_isdst = 0; // No conversion desired
363
364 time_t tEpoch = mktime(&tGMT);
365
366 if (tEpoch != -1)
367 {
368 // Valid time_t value returned from mktime().
369 // mktime() expects a local time which means that tEpoch now needs
370 // to be adjusted by the difference between this time zone and GMT.
371 tEpoch -= timezone;
372 }
373
374 return tEpoch;
375 }
376 //
377 // cTle.cpp
378 // This class encapsulates a single set of standard NORAD two line elements.
379 //
380 // Copyright 1996-2005 Michael F. Henry
381 //
382 // Note: The column offsets are ZERO based.
383
384 // Name
385 const int TLE_LEN_LINE_DATA = 69; const int TLE_LEN_LINE_NAME = 22;
386
387 // Line 1
388 const int TLE1_COL_SATNUM = 2; const int TLE1_LEN_SATNUM = 5;
389 const int TLE1_COL_INTLDESC_A = 9; const int TLE1_LEN_INTLDESC_A = 2;
390 const int TLE1_COL_INTLDESC_B = 11; const int TLE1_LEN_INTLDESC_B = 3;
391 const int TLE1_COL_INTLDESC_C = 14; const int TLE1_LEN_INTLDESC_C = 3;
392 const int TLE1_COL_EPOCH_A = 18; const int TLE1_LEN_EPOCH_A = 2;
393 const int TLE1_COL_EPOCH_B = 20; const int TLE1_LEN_EPOCH_B = 12;
394 const int TLE1_COL_MEANMOTIONDT = 33; const int TLE1_LEN_MEANMOTIONDT = 10;
395 const int TLE1_COL_MEANMOTIONDT2 = 44; const int TLE1_LEN_MEANMOTIONDT2 = 8;
396 const int TLE1_COL_BSTAR = 53; const int TLE1_LEN_BSTAR = 8;
397 const int TLE1_COL_EPHEMTYPE = 62; const int TLE1_LEN_EPHEMTYPE = 1;
398 const int TLE1_COL_ELNUM = 64; const int TLE1_LEN_ELNUM = 4;
399
400 // Line 2
401 const int TLE2_COL_SATNUM = 2; const int TLE2_LEN_SATNUM = 5;
402 const int TLE2_COL_INCLINATION = 8; const int TLE2_LEN_INCLINATION = 8;
403 const int TLE2_COL_RAASCENDNODE = 17; const int TLE2_LEN_RAASCENDNODE = 8;
404 const int TLE2_COL_ECCENTRICITY = 26; const int TLE2_LEN_ECCENTRICITY = 7;
405 const int TLE2_COL_ARGPERIGEE = 34; const int TLE2_LEN_ARGPERIGEE = 8;
406 const int TLE2_COL_MEANANOMALY = 43; const int TLE2_LEN_MEANANOMALY = 8;
407 const int TLE2_COL_MEANMOTION = 52; const int TLE2_LEN_MEANMOTION = 11;
408 const int TLE2_COL_REVATEPOCH = 63; const int TLE2_LEN_REVATEPOCH = 5;
409
410 /////////////////////////////////////////////////////////////////////////////
411 cTle::cTle(string& strName, string& strLine1, string& strLine2)
412 {
413 m_strName = strName;
414 m_strLine1 = strLine1;
415 m_strLine2 = strLine2;
416
417 Initialize();
418 }
419
420 /////////////////////////////////////////////////////////////////////////////
421 cTle::cTle(const cTle &tle)
422 {
423 m_strName = tle.m_strName;
424 m_strLine1 = tle.m_strLine1;
425 m_strLine2 = tle.m_strLine2;
426
427 for (int fld = FLD_FIRST; fld < FLD_LAST; fld++)
428 {
429 m_Field[fld] = tle.m_Field[fld];
430 }
431
432 m_mapCache = tle.m_mapCache;
433 }
434
435 /////////////////////////////////////////////////////////////////////////////
436 cTle::~cTle()
437 {
438 }
439
440 /////////////////////////////////////////////////////////////////////////////
441 // getField()
442 // Return requested field as a double (function return value) or as a text
443 // string (*pstr) in the units requested (eUnit). Set 'bStrUnits' to true
444 // to have units appended to text string.
445 //
446 // Note: numeric return values are cached; asking for the same field more
447 // than once incurs minimal overhead.
448 double cTle::getField(eField fld,
449 eUnits units, /* = U_NATIVE */
450 string *pstr /* = NULL */,
451 bool bStrUnits /* = false */) const
452 {
453 assert((FLD_FIRST <= fld) && (fld < FLD_LAST));
454 assert((U_FIRST <= units) && (units < U_LAST));
455
456 if (pstr)
457 {
458 // Return requested field in string form.
459 *pstr = m_Field[fld];
460
461 if (bStrUnits)
462 *pstr += getUnits(fld);
463
464 return 0.0;
465 }
466 else
467 {
468 // Return requested field in floating-point form.
469 // Return cache contents if it exists, else populate cache
470 FldKey key = Key(units, fld);
471
472 if (m_mapCache.find(key) == m_mapCache.end())
473 {
474 // Value not in cache; add it
475 double valNative = atof(m_Field[fld].c_str());
476 double valConv = ConvertUnits(valNative, fld, units);
477 m_mapCache[key] = valConv;
478
479 return valConv;
480 }
481 else
482 {
483 // return cached value
484 return m_mapCache[key];
485 }
486 }
487 }
488
489 //////////////////////////////////////////////////////////////////////////////
490 // Convert the given field into the requested units. It is assumed that
491 // the value being converted is in the TLE format's "native" form.
492 double cTle::ConvertUnits(double valNative, // value to convert
493 eField fld, // what field the value is
494 eUnits units) // what units to convert to
495 {
496 switch (fld)
497 {
498 case FLD_I:
499 case FLD_RAAN:
500 case FLD_ARGPER:
501 case FLD_M:
502 {
503 // The native TLE format is DEGREES
504 if (units == U_RAD)
505 return valNative * RADS_PER_DEG;
506 }
507
508 case FLD_NORADNUM:
509 case FLD_INTLDESC:
510 case FLD_SET:
511 case FLD_EPOCHYEAR:
512 case FLD_EPOCHDAY:
513 case FLD_ORBITNUM:
514 case FLD_E:
515 case FLD_MMOTION:
516 case FLD_MMOTIONDT:
517 case FLD_MMOTIONDT2:
518 case FLD_BSTAR:
519 case FLD_LAST:
520 { // do nothing
521
522 }
523
524 }
525
526 return valNative; // return value in unconverted native format
527 }
528
529 //////////////////////////////////////////////////////////////////////////////
530 string cTle::getUnits(eField fld) const
531 {
532 static const string strDegrees = " degrees";
533 static const string strRevsPerDay = " revs / day";
534 static const string strNull;
535
536 switch (fld)
537 {
538 case FLD_I:
539 case FLD_RAAN:
540 case FLD_ARGPER:
541 case FLD_M:
542 return strDegrees;
543
544 case FLD_MMOTION:
545 return strRevsPerDay;
546
547 default:
548 return strNull;
549 }
550 }
551
552 /////////////////////////////////////////////////////////////////////////////
553 // ExpToDecimal()
554 // Converts TLE-style exponential notation of the form [ |-]00000[+|-]0 to
555 // decimal notation. Assumes implied decimal point to the left of the first
556 // number in the string, i.e.,
557 // " 12345-3" = 0.00012345
558 // "-23429-5" = -0.0000023429
559 // " 40436+1" = 4.0436
560 string cTle::ExpToDecimal(const string &str)
561 {
562 const int COL_EXP_SIGN = 6;
563 const int LEN_EXP = 2;
564
565 const int LEN_BUFREAL = 32; // max length of buffer to hold floating point
566 // representation of input string.
567 int nMan;
568 int nExp;
569
570 // sscanf(%d) will read up to the exponent sign
571 sscanf(str.c_str(), "%d", &nMan);
572
573 double dblMan = nMan;
574 bool bNeg = (nMan < 0);
575
576 if (bNeg)
577 dblMan *= -1;
578
579 // Move decimal place to left of first digit
580 while (dblMan >= 1.0)
581 dblMan /= 10.0;
582
583 if (bNeg)
584 dblMan *= -1;
585
586 // now read exponent
587 sscanf(str.substr(COL_EXP_SIGN, LEN_EXP).c_str(), "%d", &nExp);
588
589 double dblVal = dblMan * pow(10.0, nExp);
590 char szVal[LEN_BUFREAL];
591
592 snprintf(szVal, sizeof(szVal), "%.9f", dblVal);
593
594 string strVal = szVal;
595
596 return strVal;
597
598 } // ExpToDecimal()
599
600 /////////////////////////////////////////////////////////////////////////////
601 // Initialize()
602 // Initialize the string array.
603 void cTle::Initialize()
604 {
605 // Have we already been initialized?
606 if (m_Field[FLD_NORADNUM].size())
607 return;
608
609 assert(!m_strName.empty());
610 assert(!m_strLine1.empty());
611 assert(!m_strLine2.empty());
612
613 m_Field[FLD_NORADNUM] = m_strLine1.substr(TLE1_COL_SATNUM, TLE1_LEN_SATNUM);
614 m_Field[FLD_INTLDESC] = m_strLine1.substr(TLE1_COL_INTLDESC_A,
615 TLE1_LEN_INTLDESC_A +
616 TLE1_LEN_INTLDESC_B +
617 TLE1_LEN_INTLDESC_C);
618 m_Field[FLD_EPOCHYEAR] =
619 m_strLine1.substr(TLE1_COL_EPOCH_A, TLE1_LEN_EPOCH_A);
620
621 m_Field[FLD_EPOCHDAY] =
622 m_strLine1.substr(TLE1_COL_EPOCH_B, TLE1_LEN_EPOCH_B);
623
624 if (m_strLine1[TLE1_COL_MEANMOTIONDT] == '-')
625 {
626 // value is negative
627 m_Field[FLD_MMOTIONDT] = "-0";
628 }
629 else
630 m_Field[FLD_MMOTIONDT] = "0";
631
632 m_Field[FLD_MMOTIONDT] += m_strLine1.substr(TLE1_COL_MEANMOTIONDT + 1,
633 TLE1_LEN_MEANMOTIONDT);
634
635 // decimal point assumed; exponential notation
636 m_Field[FLD_MMOTIONDT2] = ExpToDecimal(
637 m_strLine1.substr(TLE1_COL_MEANMOTIONDT2,
638 TLE1_LEN_MEANMOTIONDT2));
639 // decimal point assumed; exponential notation
640 m_Field[FLD_BSTAR] = ExpToDecimal(m_strLine1.substr(TLE1_COL_BSTAR,
641 TLE1_LEN_BSTAR));
642 //TLE1_COL_EPHEMTYPE
643 //TLE1_LEN_EPHEMTYPE
644 m_Field[FLD_SET] = m_strLine1.substr(TLE1_COL_ELNUM, TLE1_LEN_ELNUM);
645
646 TrimLeft(m_Field[FLD_SET]);
647
648 //TLE2_COL_SATNUM
649 //TLE2_LEN_SATNUM
650
651 m_Field[FLD_I] = m_strLine2.substr(TLE2_COL_INCLINATION,
652 TLE2_LEN_INCLINATION);
653 TrimLeft(m_Field[FLD_I]);
654
655 m_Field[FLD_RAAN] = m_strLine2.substr(TLE2_COL_RAASCENDNODE,
656 TLE2_LEN_RAASCENDNODE);
657 TrimLeft(m_Field[FLD_RAAN]);
658
659 // decimal point is assumed
660 m_Field[FLD_E] = "0.";
661 m_Field[FLD_E] += m_strLine2.substr(TLE2_COL_ECCENTRICITY,
662 TLE2_LEN_ECCENTRICITY);
663
664 m_Field[FLD_ARGPER] = m_strLine2.substr(TLE2_COL_ARGPERIGEE,
665 TLE2_LEN_ARGPERIGEE);
666 TrimLeft(m_Field[FLD_ARGPER]);
667
668 m_Field[FLD_M] = m_strLine2.substr(TLE2_COL_MEANANOMALY,
669 TLE2_LEN_MEANANOMALY);
670 TrimLeft(m_Field[FLD_M]);
671
672 m_Field[FLD_MMOTION] = m_strLine2.substr(TLE2_COL_MEANMOTION,
673 TLE2_LEN_MEANMOTION);
674 TrimLeft(m_Field[FLD_MMOTION]);
675
676 m_Field[FLD_ORBITNUM] = m_strLine2.substr(TLE2_COL_REVATEPOCH,
677 TLE2_LEN_REVATEPOCH);
678 TrimLeft(m_Field[FLD_ORBITNUM]);
679
680 } // InitStrVars()
681
682 /////////////////////////////////////////////////////////////////////////////
683 // IsTleFormat()
684 // Returns true if "str" is a valid data line of a two-line element set,
685 // else false.
686 //
687 // To be valid a line must:
688 // Have as the first character the line number
689 // Have as the second character a blank
690 // Be TLE_LEN_LINE_DATA characters long
691 // Have a valid checksum (note: no longer required as of 12/96)
692 //
693 bool cTle::IsValidLine(string& str, eTleLine line)
694 {
695 TrimLeft(str);
696 TrimRight(str);
697
698 size_t nLen = str.size();
699
700 if (nLen != (uint)TLE_LEN_LINE_DATA)
701 return false;
702
703 // First char in string must be line number
704 if ((str[0] - '0') != line)
705 return false;
706
707 // Second char in string must be blank
708 if (str[1] != ' ')
709 return false;
710
711 /*
712 NOTE: 12/96
713 The requirement that the last char in the line data must be a valid
714 checksum is too restrictive.
715
716 // Last char in string must be checksum
717 int nSum = CheckSum(str);
718
719 if (nSum != (str[TLE_LEN_LINE_DATA - 1] - '0'))
720 return false;
721 */
722
723 return true;
724
725 } // IsTleFormat()
726
727 /////////////////////////////////////////////////////////////////////////////
728 // CheckSum()
729 // Calculate the check sum for a given line of TLE data, the last character
730 // of which is the current checksum. (Although there is no check here,
731 // the current checksum should match the one we calculate.)
732 // The checksum algorithm:
733 // Each number in the data line is summed, modulo 10.
734 // Non-numeric characters are zero, except minus signs, which are 1.
735 //
736 int cTle::CheckSum(const string& str)
737 {
738 // The length is "- 1" because we don't include the current (existing)
739 // checksum character in the checksum calculation.
740 size_t len = str.size() - 1;
741 int xsum = 0;
742
743 for (size_t i = 0; i < len; i++)
744 {
745 char ch = str[i];
746 if (isdigit(ch))
747 xsum += (ch - '0');
748 else if (ch == '-')
749 xsum++;
750 }
751
752 return (xsum % 10);
753
754 } // CheckSum()
755
756 /////////////////////////////////////////////////////////////////////////////
757 void cTle::TrimLeft(string& s)
758 {
759 while (s[0] == ' ')
760 s.erase(0, 1);
761 }
762
763 /////////////////////////////////////////////////////////////////////////////
764 void cTle::TrimRight(string& s)
765 {
766 while (s[s.size() - 1] == ' ')
767 s.erase(s.size() - 1);
768 }
769
770 //
771 // cEci.cpp
772 //
773 // Copyright (c) 2002-2003 Michael F. Henry
774 //
775 //////////////////////////////////////////////////////////////////////
776 // cEci Class
777 //////////////////////////////////////////////////////////////////////
778 cEci::cEci(const cVector &pos,
779 const cVector &vel,
780 const cJulian &date,
781 bool IsAeUnits /* = true */)
782 {
783 m_pos = pos;
784 m_vel = vel;
785 m_date = date;
786 m_VecUnits = (IsAeUnits ? UNITS_AE : UNITS_NONE);
787 }
788
789 //////////////////////////////////////////////////////////////////////
790 // cEci(cCoordGeo&, cJulian&)
791 // Calculate the ECI coordinates of the location "geo" at time "date".
792 // Assumes geo coordinates are km-based.
793 // Assumes the earth is an oblate spheroid as defined in WGS '72.
794 // Reference: The 1992 Astronomical Almanac, page K11
795 // Reference: www.celestrak.com (Dr. TS Kelso)
796 cEci::cEci(const cCoordGeo &geo, const cJulian &date)
797 {
798 m_VecUnits = UNITS_KM;
799
800 double mfactor = TWOPI * (OMEGA_E / SEC_PER_DAY);
801 double lat = geo.m_Lat;
802 double lon = geo.m_Lon;
803 double alt = geo.m_Alt;
804
805 // Calculate Local Mean Sidereal Time (theta)
806 double theta = date.toLMST(lon);
807 double c = 1.0 / sqrt(1.0 + F * (F - 2.0) * sqr(sin(lat)));
808 double s = sqr(1.0 - F) * c;
809 double achcp = (XKMPER_WGS72 * c + alt) * cos(lat);
810
811 m_date = date;
812
813 m_pos.m_x = achcp * cos(theta); // km
814 m_pos.m_y = achcp * sin(theta); // km
815 m_pos.m_z = (XKMPER_WGS72 * s + alt) * sin(lat); // km
816 m_pos.m_w = sqrt(sqr(m_pos.m_x) +
817 sqr(m_pos.m_y) +
818 sqr(m_pos.m_z)); // range, km
819
820 m_vel.m_x = -mfactor * m_pos.m_y; // km / sec
821 m_vel.m_y = mfactor * m_pos.m_x;
822 m_vel.m_z = 0.0;
823 m_vel.m_w = sqrt(sqr(m_vel.m_x) + // range rate km/sec^2
824 sqr(m_vel.m_y));
825 }
826
827 //////////////////////////////////////////////////////////////////////////////
828 // toGeo()
829 // Return the corresponding geodetic position (based on the current ECI
830 // coordinates/Julian date).
831 // Assumes the earth is an oblate spheroid as defined in WGS '72.
832 // Side effects: Converts the position and velocity vectors to km-based units.
833 // Reference: The 1992 Astronomical Almanac, page K12.
834 // Reference: www.celestrak.com (Dr. TS Kelso)
835 cCoordGeo cEci::toGeo()
836 {
837 ae2km(); // Vectors must be in kilometer-based units
838
839 double theta = AcTan(m_pos.m_y, m_pos.m_x);
840 double lon = fmod(theta - m_date.toGMST(), TWOPI);
841
842 if (lon < 0.0)
843 lon += TWOPI; // "wrap" negative modulo
844
845 double r = sqrt(sqr(m_pos.m_x) + sqr(m_pos.m_y));
846 double e2 = F * (2.0 - F);
847 double lat = AcTan(m_pos.m_z, r);
848
849 const double delta = 1.0e-07;
850 double phi;
851 double c;
852
853 do
854 {
855 phi = lat;
856 c = 1.0 / sqrt(1.0 - e2 * sqr(sin(phi)));
857 lat = AcTan(m_pos.m_z + XKMPER_WGS72 * c * e2 * sin(phi), r);
858 }
859 while (fabs(lat - phi) > delta);
860
861 double alt = r / cos(lat) - XKMPER_WGS72 * c;
862
863 return cCoordGeo(lat, lon, alt); // radians, radians, kilometers
864 }
865
866 //////////////////////////////////////////////////////////////////////////////
867 // ae2km()
868 // Convert the position and velocity vector units from AE-based units
869 // to kilometer based units.
870 void cEci::ae2km()
871 {
872 if (UnitsAreAe())
873 {
874 MulPos(XKMPER_WGS72 / AE); // km
875 MulVel((XKMPER_WGS72 / AE) * (MIN_PER_DAY / 86400)); // km/sec
876 m_VecUnits = UNITS_KM;
877 }
878 }

  ViewVC Help
Powered by ViewVC 1.1.23