diff --git a/SGDP4.cpp b/SGDP4.cpp index 2c64386..2b8abbd 100644 --- a/SGDP4.cpp +++ b/SGDP4.cpp @@ -51,14 +51,14 @@ void SGDP4::SetTle(const Tle& tle) { /* * extract and format tle data */ - mean_anomoly_ = tle.GetField(Tle::FLD_M, Tle::U_RAD); - ascending_node_ = tle.GetField(Tle::FLD_RAAN, Tle::U_RAD); - argument_perigee_ = tle.GetField(Tle::FLD_ARGPER, Tle::U_RAD); - eccentricity_ = tle.GetField(Tle::FLD_E); - inclination_ = tle.GetField(Tle::FLD_I, Tle::U_RAD); - mean_motion_ = tle.GetField(Tle::FLD_MMOTION) * TWOPI / Globals::MIN_PER_DAY(); - bstar_ = tle.GetField(Tle::FLD_BSTAR); - epoch_ = tle.GetEpoch(); + mean_anomoly_ = tle.MeanAnomaly(false); + ascending_node_ = tle.RightAscendingNode(false); + argument_perigee_ = tle.ArgumentPerigee(false); + eccentricity_ = tle.Eccentricity(); + inclination_ = tle.Inclination(false); + mean_motion_ = tle.MeanMotion() * TWOPI / Globals::MIN_PER_DAY(); + bstar_ = tle.BStar(); + epoch_ = tle.Epoch(); /* * error checks diff --git a/Tle.cpp b/Tle.cpp index 62d9b63..ec7545c 100644 --- a/Tle.cpp +++ b/Tle.cpp @@ -3,7 +3,17 @@ #include #include +Tle::Tle(const std::string& line_one, const std::string& line_two) { + + name_.erase(); + line_one_ = line_one; + line_two_ = line_two; + + Initialize(); +} + Tle::Tle(const std::string& name, const std::string& line_one, const std::string& line_two) { + name_ = name; line_one_ = line_one; line_two_ = line_two; @@ -12,105 +22,48 @@ Tle::Tle(const std::string& name, const std::string& line_one, const std::string } Tle::Tle(const Tle& tle) { + name_ = tle.name_; line_one_ = tle.line_one_; line_two_ = tle.line_two_; - date_ = tle.date_; - for (int fld = FLD_FIRST; fld < FLD_LAST; fld++) { - fields_[fld] = tle.fields_[fld]; - } + norad_number_ = tle.norad_number_; + international_designator_ = tle.international_designator_; + epoch_ = tle.epoch_; + mean_motion_dot_ = tle.mean_motion_dot_; + mean_motion_dot2_ = tle.mean_motion_dot2_; + bstar_ = tle.bstar_; + inclination_ = tle.inclination_; + right_ascending_node_ = tle.right_ascending_node_; + eccentricity_ = tle.eccentricity_; + argument_perigee_ = tle.argument_perigee_; + mean_anomaly_ = tle.mean_anomaly_; + mean_motion_ = tle.mean_motion_; + orbit_number_ = tle.orbit_number_; } Tle::~Tle() { } -double Tle::GetField(TleField fld, TleUnits units) const { - assert((FLD_FIRST <= fld) && (fld < FLD_LAST)); - assert((U_FIRST <= units) && (units < U_LAST)); - - double value = fields_[fld].second; - double converted = 0.0; - - switch (fld) { - case FLD_I: - case FLD_RAAN: - case FLD_ARGPER: - case FLD_M: - { - /* - * native format is degrees - */ - if (units == U_RAD) - converted = Globals::Deg2Rad(value); - break; - } - default: - { - converted = value; - break; - } - } - - return converted; -} - /* - * get field represented as a string - */ -std::string Tle::GetFieldString(TleField fld, bool append_units) const { - assert((FLD_FIRST <= fld) && (fld < FLD_LAST)); - - std::string str = fields_[fld].first; - - if (append_units) - str += GetUnits(fld); - - return str; -} - -/* - * get the units for a field - */ -std::string Tle::GetUnits(TleField field) const { - static const std::string strDegrees = " degrees"; - static const std::string strRevsPerDay = " revs / day"; - static const std::string strNull = ""; - - switch (field) { - case FLD_I: - case FLD_RAAN: - case FLD_ARGPER: - case FLD_M: - return strDegrees; - - case FLD_MMOTION: - return strRevsPerDay; - - default: - return strNull; - } -} - -/* - * convert a raw string into an exponent string + * convert a tle raw string into an exponent string */ std::string Tle::ExpToDecimal(const std::string& str) { - static const int kColumnSign = 0; - static const int kLengthSign = 1; - static const int kColumnMantissa = 1; - static const int kLengthMantissa = 5; - static const int kColumnExp = 6; - static const int kLengthExp = 2; + + static const int START_SIGN = 0; + static const int LENGTH_SIGN = 1; + static const int START_MANTISSA = 1; + static const int LENGTH_MANTISSA = 5; + static const int START_EXP = 6; + static const int LENGTH_EXP = 2; assert(8 == str.length()); - std::string value; - value = str.substr(kColumnSign, kLengthSign); + std::string value = str.substr(START_SIGN, LENGTH_SIGN); value += "."; - value += str.substr(kColumnMantissa, kLengthMantissa); + value += str.substr(START_MANTISSA, LENGTH_MANTISSA); value += "e"; - value += str.substr(kColumnExp, kLengthExp); + value += str.substr(START_EXP, LENGTH_EXP); return value; } @@ -119,6 +72,12 @@ std::string Tle::ExpToDecimal(const std::string& str) { * extract all variables */ void Tle::Initialize() { + + std::string temp; + + /* + * trim whitespace + */ TrimLeft(name_); TrimRight(name_); TrimLeft(line_one_); @@ -126,104 +85,129 @@ void Tle::Initialize() { TrimLeft(line_two_); TrimRight(line_two_); - assert(!name_.empty()); - assert(!line_one_.empty()); - assert(!line_two_.empty()); - - assert(IsValidLine(line_one_, Tle::LINE_ONE)); - assert(IsValidLine(line_two_, Tle::LINE_TWO)); + /* + * check the two lines are valid + */ + assert(IsValidPair(line_one_, line_two_)); /* * line 1 */ - fields_[FLD_NORADNUM].first = line_one_.substr(TLE1_COL_SATNUM, TLE1_LEN_SATNUM); - fields_[FLD_INTLDESC].first = line_one_.substr(TLE1_COL_INTLDESC_A, TLE1_LEN_INTLDESC_A + - TLE1_LEN_INTLDESC_B + TLE1_LEN_INTLDESC_C); - fields_[FLD_EPOCHYEAR].first = line_one_.substr(TLE1_COL_EPOCH_A, TLE1_LEN_EPOCH_A); + temp = line_one_.substr(TLE1_COL_NORADNUM, TLE1_LEN_NORADNUM).c_str(); + norad_number_ = atoi(temp.c_str()); + /* + * if blank use norad number + */ + if (name_.empty()) + name_ = temp; - fields_[FLD_EPOCHDAY].first = line_one_.substr(TLE1_COL_EPOCH_B, TLE1_LEN_EPOCH_B); + international_designator_ = line_one_.substr(TLE1_COL_INTLDESC_A, TLE1_LEN_INTLDESC_A + TLE1_LEN_INTLDESC_B + TLE1_LEN_INTLDESC_C); + + int year = atoi(line_one_.substr(TLE1_COL_EPOCH_A, TLE1_LEN_EPOCH_A).c_str()); + double day = atof(line_one_.substr(TLE1_COL_EPOCH_B, TLE1_LEN_EPOCH_B).c_str()); + /* + * generate julian date for epoch + */ + if (year < 57) + year += 2000; + else + year += 1900; + + epoch_ = Julian(year, day); if (line_one_[TLE1_COL_MEANMOTIONDT] == '-') { - // value is negative - fields_[FLD_MMOTIONDT].first = "-0"; + temp = "-0"; } else - fields_[FLD_MMOTIONDT].first = "0"; + temp = "0"; + temp += line_one_.substr(TLE1_COL_MEANMOTIONDT + 1, TLE1_LEN_MEANMOTIONDT); + mean_motion_dot_ = atof(temp.c_str()); - fields_[FLD_MMOTIONDT].first += line_one_.substr(TLE1_COL_MEANMOTIONDT + 1, TLE1_LEN_MEANMOTIONDT); + temp = ExpToDecimal(line_one_.substr(TLE1_COL_MEANMOTIONDT2, TLE1_LEN_MEANMOTIONDT2)); + mean_motion_dot2_ = atof(temp.c_str()); - // decimal point assumed; exponential notation - fields_[FLD_MMOTIONDT2].first = ExpToDecimal(line_one_.substr(TLE1_COL_MEANMOTIONDT2, TLE1_LEN_MEANMOTIONDT2)); - - // decimal point assumed; exponential notation - fields_[FLD_BSTAR].first = ExpToDecimal(line_one_.substr(TLE1_COL_BSTAR, TLE1_LEN_BSTAR)); - - fields_[FLD_SET].first = line_one_.substr(TLE1_COL_ELNUM, TLE1_LEN_ELNUM); - TrimLeft(fields_[FLD_SET].first); + temp = ExpToDecimal(line_one_.substr(TLE1_COL_BSTAR, TLE1_LEN_BSTAR).c_str()); + bstar_ = atof(temp.c_str()); /* * line 2 */ - fields_[FLD_I].first = line_two_.substr(TLE2_COL_INCLINATION, TLE2_LEN_INCLINATION); - TrimLeft(fields_[FLD_I].first); + temp = line_two_.substr(TLE2_COL_INCLINATION, TLE2_LEN_INCLINATION); + TrimLeft(temp); + inclination_ = atof(temp.c_str()); - fields_[FLD_RAAN].first = line_two_.substr(TLE2_COL_RAASCENDNODE, TLE2_LEN_RAASCENDNODE); - TrimLeft(fields_[FLD_RAAN].first); + temp = line_two_.substr(TLE2_COL_RAASCENDNODE, TLE2_LEN_RAASCENDNODE); + TrimLeft(temp); + right_ascending_node_ = atof(temp.c_str()); - // decimal point is assumed - fields_[FLD_E].first = "0."; - fields_[FLD_E].first += line_two_.substr(TLE2_COL_ECCENTRICITY, TLE2_LEN_ECCENTRICITY); + temp = "0."; + temp += line_two_.substr(TLE2_COL_ECCENTRICITY, TLE2_LEN_ECCENTRICITY); + eccentricity_ = atof(temp.c_str()); - fields_[FLD_ARGPER].first = line_two_.substr(TLE2_COL_ARGPERIGEE, TLE2_LEN_ARGPERIGEE); - TrimLeft(fields_[FLD_ARGPER].first); + temp = line_two_.substr(TLE2_COL_ARGPERIGEE, TLE2_LEN_ARGPERIGEE); + TrimLeft(temp); + argument_perigee_ = atof(temp.c_str()); - fields_[FLD_M].first = line_two_.substr(TLE2_COL_MEANANOMALY, TLE2_LEN_MEANANOMALY); - TrimLeft(fields_[FLD_M].first); + temp = line_two_.substr(TLE2_COL_MEANANOMALY, TLE2_LEN_MEANANOMALY); + TrimLeft(temp); + mean_anomaly_ = atof(temp.c_str()); - fields_[FLD_MMOTION].first = line_two_.substr(TLE2_COL_MEANMOTION, TLE2_LEN_MEANMOTION); - TrimLeft(fields_[FLD_MMOTION].first); + temp = line_two_.substr(TLE2_COL_MEANMOTION, TLE2_LEN_MEANMOTION); + TrimLeft(temp); + mean_motion_ = atof(temp.c_str()); - fields_[FLD_ORBITNUM].first = line_two_.substr(TLE2_COL_REVATEPOCH, TLE2_LEN_REVATEPOCH); - TrimLeft(fields_[FLD_ORBITNUM].first); + temp = line_two_.substr(TLE2_COL_REVATEPOCH, TLE2_LEN_REVATEPOCH); + TrimLeft(temp); + orbit_number_ = atoi(temp.c_str()); +} - // update double variables - for (int fld = FLD_FIRST; fld < FLD_LAST; fld++) { - fields_[fld].second = atof(fields_[fld].first.c_str()); - } +/* + * check the two lines have matching norad numbers + * and that the lines themselves are valid + */ +bool Tle::IsValidPair(const std::string& line1, const std::string& line2) { - int year = static_cast (GetField(Tle::FLD_EPOCHYEAR)); - if (year < 57) - year += 2000; - else - year += 1900; - double day = GetField(Tle::FLD_EPOCHDAY); + if (atoi(line1.substr(TLE1_COL_NORADNUM, TLE1_LEN_NORADNUM).c_str()) != + atoi(line2.substr(TLE2_COL_NORADNUM, TLE2_LEN_NORADNUM).c_str())) + return false; - date_ = Julian(year, day); + if (!IsValidLine(line1, 1)) + return false; + + if (!IsValidLine(line2, 2)) + + return false; + + return true; } /* * validate a line */ -bool Tle::IsValidLine(std::string& str, TleLine line) { - TrimLeft(str); - TrimRight(str); +bool Tle::IsValidLine(const std::string& str, const unsigned char line_number) { - const std::string line1_validation = "1 NNNNNC NNNNAAAA NNNNN.NNNNNNNN +.NNNNNNNN +NNNNN-N +NNNNN-N N NNNNN"; - const std::string line2_validation = "2 NNNNN NNN.NNNN NNN.NNNN NNNNNNN NNN.NNNN NNN.NNNN NN.NNNNNNNNNNNNNN"; + static const std::string line1_pattern = "1 NNNNNC NNNNAAAA NNNNN.NNNNNNNN +.NNNNNNNN +NNNNN-N +NNNNN-N N NNNNN"; + static const std::string line2_pattern = "2 NNNNN NNN.NNNN NNN.NNNN NNNNNNN NNN.NNNN NNN.NNNN NN.NNNNNNNNNNNNNN"; - if (Tle::LINE_ONE == line) { - if (!ValidateLine(line1_validation, str)) + /* + * validate line against the pattern + */ + if (1 == line_number) { + if (!ValidateLine(str, line1_pattern)) return false; - } else if (Tle::LINE_TWO == line) { - if (!ValidateLine(line2_validation, str)) + } else if (2 == line_number) { + if (!ValidateLine(str, line2_pattern)) return false; } - // Last char in string must be checksum - // int chk = CheckSum(str); - // if (chk != (str[TLE_LEN_LINE_DATA - 1] - '0')) - // return false; + /* + * last char in string is modulo 10 checksum + */ + int chk = CheckSum(str); + if (chk != (str[TLE_LEN_LINE_DATA - 1] - '0')) + + return false; return true; } @@ -231,23 +215,49 @@ bool Tle::IsValidLine(std::string& str, TleLine line) { /* * validate line given a pattern */ -bool Tle::ValidateLine(const std::string& pattern, const std::string& line) { +bool Tle::ValidateLine(const std::string& line, const std::string& pattern) { + /* + * check length of lines match + */ if (pattern.length() != line.length()) { return false; } - std::string::size_type pos = 0; + std::string::const_iterator pattern_itr = pattern.begin(); + std::string::const_iterator line_itr = line.begin(); - while (pos != line.length() - 1) { - if (isdigit(pattern[pos]) || pattern[pos] == ' ' || pattern[pos] == '.') { - if (pattern[pos] != line[pos]) + while (pattern_itr != pattern.end()) { + + if (isdigit(*pattern_itr) || *pattern_itr == ' ' || + *pattern_itr == '.' || *pattern_itr == '-') { + + /* + * these characters should match exactly + */ + if (*pattern_itr != *line_itr) return false; - } else if (pattern[pos] == 'N') { - if (!isdigit(line[pos]) && line[pos] != ' ') + + } else if (*pattern_itr == 'N') { + + /* + * if pattern value is 'N' then either a number or a ' ' + */ + if (!isdigit(*line_itr) && *line_itr != ' ') + return false; + + } else if (*pattern_itr == '+') { + + /* + * if pattern value is '+' then either a '+' or a ' ' + */ + if (*line_itr != '+' && *line_itr != ' ') + return false; } - pos++; + + pattern_itr++; + line_itr++; } return true; @@ -257,14 +267,19 @@ bool Tle::ValidateLine(const std::string& pattern, const std::string& line) { * compute checksum */ int Tle::CheckSum(const std::string& str) { + size_t len = str.size() - 1; int xsum = 0; for (size_t i = 0; i < len; i++) { + char ch = str[i]; + if (isdigit(ch)) xsum += (ch - '0'); - else if (ch == '-') + else + + if (ch == '-') xsum++; } @@ -274,28 +289,35 @@ int Tle::CheckSum(const std::string& str) { /* * trim left of string */ -void Tle::TrimLeft(std::string& s) { - if (!s.empty()) { - std::string::size_type pos = s.find_first_not_of(' '); +void Tle::TrimLeft(std::string& str) { + + std::string whitespaces(" \t\f\n\r"); + + if (!str.empty()) { + std::string::size_type pos = str.find_first_not_of(whitespaces); if (pos != std::string::npos) - s.erase(0, pos); + str.erase(0, pos); + else - s.clear(); + str.clear(); } } /* * trim right of string */ -void Tle::TrimRight(std::string& s) { - if (!s.empty()) { - std::string::size_type pos = s.find_last_not_of(' '); +void Tle::TrimRight(std::string& str) { + + std::string whitespaces(" \t\f\n\r"); + + if (!str.empty()) { + std::string::size_type pos = str.find_last_not_of(whitespaces); if (pos != std::string::npos) - s.erase(pos + 1); + str.erase(pos + 1); else - s.clear(); + str.clear(); } } diff --git a/Tle.h b/Tle.h index e5b41d2..2ba7847 100644 --- a/Tle.h +++ b/Tle.h @@ -8,73 +8,14 @@ class Tle { public: + Tle(const std::string& line_one, const std::string& line_two); Tle(const std::string& name, const std::string& line_one, const std::string& line_two); Tle(const Tle& tle); virtual ~Tle(); - enum TleLine { - LINE_ZERO, - LINE_ONE, - LINE_TWO - }; - - enum TleField { - FLD_FIRST, - FLD_NORADNUM = FLD_FIRST, - FLD_INTLDESC, - FLD_SET, // Tle set number - FLD_EPOCHYEAR, // Epoch: Last two digits of year - FLD_EPOCHDAY, // Epoch: Fractional Julian Day of year - FLD_ORBITNUM, // Orbit at epoch - FLD_I, // Inclination - FLD_RAAN, // R.A. ascending node - FLD_E, // Eccentricity - FLD_ARGPER, // Argument of perigee - FLD_M, // Mean anomaly - FLD_MMOTION, // Mean motion - FLD_MMOTIONDT, // First time derivative of mean motion - FLD_MMOTIONDT2, // Second time derivative of mean motion - FLD_BSTAR, // BSTAR Drag - FLD_LAST // MUST be last - }; - - enum TleUnits { - U_FIRST, - U_RAD = U_FIRST, // radians - U_DEG, // degrees - U_NATIVE, // Tle format native units (no conversion) - U_LAST // MUST be last - }; - /* - * initialize from raw tle strings + * get raw strings */ - void Initialize(); - - /* - * get field represented as a double - */ - double GetField(TleField fld, TleUnits unit = U_NATIVE) const; - /* - * get field represented as a string - */ - std::string GetFieldString(TleField fld, bool append_units) const; - - /* - * get epoch of tle - */ - Julian GetEpoch() const { - return date_; - } - - /* - * compute checksum - */ - static int CheckSum(const std::string& str); - static bool IsValidLine(std::string& str, TleLine line); - static void TrimLeft(std::string& str); - static void TrimRight(std::string& str); - std::string GetName() const { return name_; } @@ -87,28 +28,100 @@ public: return line_two_; } - double GetNoradNumber() const { - return GetField(Tle::FLD_NORADNUM); + /* + * get tle values + */ + double NoradNumber() const { + return norad_number_; } + std::string InternationlDesignator() const { + return international_designator_; + } + + Julian Epoch()const { + return epoch_; + } + + double MeanMotionDot()const { + return mean_motion_dot_; + } + + double MeanMotionDot2()const { + return mean_motion_dot2_; + } + + double BStar()const { + return bstar_; + } + + double Inclination(const bool in_degrees)const { + if (in_degrees) + return inclination_; + else + return Globals::Deg2Rad(inclination_); + } + + double RightAscendingNode(const bool in_degrees)const { + if (in_degrees) + return right_ascending_node_; + else + return Globals::Deg2Rad(right_ascending_node_); + } + + double Eccentricity()const { + return eccentricity_; + } + + double ArgumentPerigee(const bool in_degrees)const { + if (in_degrees) + return argument_perigee_; + else + return Globals::Deg2Rad(argument_perigee_); + } + + double MeanAnomaly(const bool in_degrees)const { + if (in_degrees) + return mean_anomaly_; + else + return Globals::Deg2Rad(mean_anomaly_); + } + + double MeanMotion()const { + return mean_motion_; + } + + unsigned int OrbitNumber()const { + return orbit_number_; + } + + /* + * helper / validation methods + */ + static bool IsValidPair(const std::string& line1, const std::string& line2); + static bool IsValidLine(const std::string& str, unsigned char line_number); + static void TrimLeft(std::string& str); + static void TrimRight(std::string& str); + private: /* - * get units for a field + * initialize from raw tle strings */ - std::string GetUnits(TleField field) const; + void Initialize(); /* - * get raw value for a field - */ - double GetFieldNumeric(TleField) const; - /* - * convert a raw string into an exponent string + * format a raw string into an exponent string */ static std::string ExpToDecimal(const std::string&); /* - * validate a line + * validate a line against a pattern */ - static bool ValidateLine(const std::string& pattern, const std::string& line); + static bool ValidateLine(const std::string& line, const std::string& pattern); + /* + * compute checksum + */ + static int CheckSum(const std::string& str); +private: /* * raw tle data */ @@ -117,12 +130,21 @@ private: std::string line_two_; /* - * epoch of tle + * extracted values all in native units */ - Julian date_; - - // array of tle values in native string and double form - std::pair fields_[FLD_LAST]; + double norad_number_; + std::string international_designator_; + Julian epoch_; + double mean_motion_dot_; + double mean_motion_dot2_; + double bstar_; + double inclination_; + double right_ascending_node_; + double eccentricity_; + double argument_perigee_; + double mean_anomaly_; + double mean_motion_; + unsigned int orbit_number_; /* * name line @@ -133,8 +155,8 @@ private: /* * line 1 */ - static const unsigned int TLE1_COL_SATNUM = 2; - static const unsigned int TLE1_LEN_SATNUM = 5; + static const unsigned int TLE1_COL_NORADNUM = 2; + static const unsigned int TLE1_LEN_NORADNUM = 5; static const unsigned int TLE1_COL_INTLDESC_A = 9; static const unsigned int TLE1_LEN_INTLDESC_A = 2; static const unsigned int TLE1_COL_INTLDESC_B = 11; @@ -159,8 +181,8 @@ private: /* * line 2 */ - static const unsigned int TLE2_COL_SATNUM = 2; - static const unsigned int TLE2_LEN_SATNUM = 5; + static const unsigned int TLE2_COL_NORADNUM = 2; + static const unsigned int TLE2_LEN_NORADNUM = 5; static const unsigned int TLE2_COL_INCLINATION = 8; static const unsigned int TLE2_LEN_INCLINATION = 8; static const unsigned int TLE2_COL_RAASCENDNODE = 17;