diff --git a/Rewrite.vcxproj b/Rewrite.vcxproj index 998bf96..26657a3 100644 --- a/Rewrite.vcxproj +++ b/Rewrite.vcxproj @@ -78,12 +78,14 @@ + + diff --git a/Rewrite.vcxproj.filters b/Rewrite.vcxproj.filters index 7e3811e..0d3971e 100644 --- a/Rewrite.vcxproj.filters +++ b/Rewrite.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + @@ -44,5 +47,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Tle.cpp b/Tle.cpp new file mode 100644 index 0000000..9abccf2 --- /dev/null +++ b/Tle.cpp @@ -0,0 +1,289 @@ +#include "Tle.h" + +#include +#include + +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; + + Initialize(); +} + +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]; + } +} + +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 + */ +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; + + assert(8 == str.length()); + + std::string value; + value = str.substr(kColumnSign, kLengthSign); + value += "."; + value += str.substr(kColumnMantissa, kLengthMantissa); + value += "e"; + value += str.substr(kColumnExp, kLengthExp); + + return value; +} + +/* + * extract all variables + */ +void Tle::Initialize() { + assert(!name_.empty()); + assert(!line_one_.empty()); + assert(!line_two_.empty()); + + /* + * 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); + + fields_[FLD_EPOCHDAY].first = line_one_.substr(TLE1_COL_EPOCH_B, TLE1_LEN_EPOCH_B); + + if (line_one_[TLE1_COL_MEANMOTIONDT] == '-') { + // value is negative + fields_[FLD_MMOTIONDT].first = "-0"; + } else + fields_[FLD_MMOTIONDT].first = "0"; + + fields_[FLD_MMOTIONDT].first += line_one_.substr(TLE1_COL_MEANMOTIONDT + 1, TLE1_LEN_MEANMOTIONDT); + + // 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); + + /* + * line 2 + */ + + fields_[FLD_I].first = line_two_.substr(TLE2_COL_INCLINATION, TLE2_LEN_INCLINATION); + TrimLeft(fields_[FLD_I].first); + + fields_[FLD_RAAN].first = line_two_.substr(TLE2_COL_RAASCENDNODE, TLE2_LEN_RAASCENDNODE); + TrimLeft(fields_[FLD_RAAN].first); + + // decimal point is assumed + fields_[FLD_E].first = "0."; + fields_[FLD_E].first += line_two_.substr(TLE2_COL_ECCENTRICITY, TLE2_LEN_ECCENTRICITY); + + fields_[FLD_ARGPER].first = line_two_.substr(TLE2_COL_ARGPERIGEE, TLE2_LEN_ARGPERIGEE); + TrimLeft(fields_[FLD_ARGPER].first); + + fields_[FLD_M].first = line_two_.substr(TLE2_COL_MEANANOMALY, TLE2_LEN_MEANANOMALY); + TrimLeft(fields_[FLD_M].first); + + fields_[FLD_MMOTION].first = line_two_.substr(TLE2_COL_MEANMOTION, TLE2_LEN_MEANMOTION); + TrimLeft(fields_[FLD_MMOTION].first); + + fields_[FLD_ORBITNUM].first = line_two_.substr(TLE2_COL_REVATEPOCH, TLE2_LEN_REVATEPOCH); + TrimLeft(fields_[FLD_ORBITNUM].first); + + // update double variables + for (int fld = FLD_FIRST; fld < FLD_LAST; fld++) { + fields_[fld].second = atof(fields_[fld].first.c_str()); + } + + int epochYear = (int) GetField(Tle::FLD_EPOCHYEAR); + double epochDay = GetField(Tle::FLD_EPOCHDAY); + + if (epochYear < 57) + epochYear += 2000; + else + epochYear += 1900; + + date_ = Julian(epochYear, epochDay); +} + +/* + * validate a line + */ +bool Tle::IsValidLine(std::string& str, TleLine line) { + TrimLeft(str); + TrimRight(str); + + 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"; + + if (Tle::LINE_ONE == line) { + if (!ValidateLine(line1_validation, str)) + return false; + } else if (Tle::LINE_TWO == line) { + if (!ValidateLine(line2_validation, str)) + return false; + } + + // Last char in string must be checksum + // int chk = CheckSum(str); + // if (chk != (str[TLE_LEN_LINE_DATA - 1] - '0')) + // return false; + + return true; +} + +/* + * validate line given a pattern + */ +bool Tle::ValidateLine(const std::string& pattern, const std::string& line) { + assert(pattern.length() == line.length()); + + std::string::size_type pos = 0; + + while (pos != line.length() - 1) { + if (isdigit(pattern[pos]) || pattern[pos] == ' ' || pattern[pos] == '.') { + if (pattern[pos] != line[pos]) + return false; + } else if (pattern[pos] == 'N') { + if (!isdigit(line[pos]) && line[pos] != ' ') + return false; + } + pos++; + } + + return true; +} + +/* + * 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 == '-') + xsum++; + } + + return (xsum % 10); +} + +/* + * trim left of string + */ +void Tle::TrimLeft(std::string& s) { + if (!s.empty()) { + std::string::size_type pos = s.find_first_not_of(' '); + + if (pos != std::string::npos) + s.erase(0, pos); + else + s.clear(); + } +} + +/* + * trim right of string + */ +void Tle::TrimRight(std::string& s) { + if (!s.empty()) { + std::string::size_type pos = s.find_last_not_of(' '); + + if (pos != std::string::npos) + s.erase(pos + 1); + else + s.clear(); + } +} + diff --git a/Tle.h b/Tle.h new file mode 100644 index 0000000..e5b41d2 --- /dev/null +++ b/Tle.h @@ -0,0 +1,182 @@ +#ifndef TLE_H_ +#define TLE_H_ + +#include "Globals.h" +#include "Julian.h" + +#include + +class Tle { +public: + 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 + */ + 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_; + } + + std::string GetLine1() const { + return line_one_; + } + + std::string GetLine2() const { + return line_two_; + } + + double GetNoradNumber() const { + return GetField(Tle::FLD_NORADNUM); + } + +private: + /* + * get units for a field + */ + std::string GetUnits(TleField field) const; + /* + * get raw value for a field + */ + double GetFieldNumeric(TleField) const; + /* + * convert a raw string into an exponent string + */ + static std::string ExpToDecimal(const std::string&); + /* + * validate a line + */ + static bool ValidateLine(const std::string& pattern, const std::string& line); + + /* + * raw tle data + */ + std::string name_; + std::string line_one_; + std::string line_two_; + + /* + * epoch of tle + */ + Julian date_; + + // array of tle values in native string and double form + std::pair fields_[FLD_LAST]; + + /* + * name line + */ + static const unsigned int TLE_LEN_LINE_DATA = 69; + static const unsigned int TLE_LEN_LINE_NAME = 22; + + /* + * line 1 + */ + static const unsigned int TLE1_COL_SATNUM = 2; + static const unsigned int TLE1_LEN_SATNUM = 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; + static const unsigned int TLE1_LEN_INTLDESC_B = 3; + static const unsigned int TLE1_COL_INTLDESC_C = 14; + static const unsigned int TLE1_LEN_INTLDESC_C = 3; + static const unsigned int TLE1_COL_EPOCH_A = 18; + static const unsigned int TLE1_LEN_EPOCH_A = 2; + static const unsigned int TLE1_COL_EPOCH_B = 20; + static const unsigned int TLE1_LEN_EPOCH_B = 12; + static const unsigned int TLE1_COL_MEANMOTIONDT = 33; + static const unsigned int TLE1_LEN_MEANMOTIONDT = 10; + static const unsigned int TLE1_COL_MEANMOTIONDT2 = 44; + static const unsigned int TLE1_LEN_MEANMOTIONDT2 = 8; + static const unsigned int TLE1_COL_BSTAR = 53; + static const unsigned int TLE1_LEN_BSTAR = 8; + static const unsigned int TLE1_COL_EPHEMTYPE = 62; + static const unsigned int TLE1_LEN_EPHEMTYPE = 1; + static const unsigned int TLE1_COL_ELNUM = 64; + static const unsigned int TLE1_LEN_ELNUM = 4; + + /* + * line 2 + */ + static const unsigned int TLE2_COL_SATNUM = 2; + static const unsigned int TLE2_LEN_SATNUM = 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; + static const unsigned int TLE2_LEN_RAASCENDNODE = 8; + static const unsigned int TLE2_COL_ECCENTRICITY = 26; + static const unsigned int TLE2_LEN_ECCENTRICITY = 7; + static const unsigned int TLE2_COL_ARGPERIGEE = 34; + static const unsigned int TLE2_LEN_ARGPERIGEE = 8; + static const unsigned int TLE2_COL_MEANANOMALY = 43; + static const unsigned int TLE2_LEN_MEANANOMALY = 8; + static const unsigned int TLE2_COL_MEANMOTION = 52; + static const unsigned int TLE2_LEN_MEANMOTION = 11; + static const unsigned int TLE2_COL_REVATEPOCH = 63; + static const unsigned int TLE2_LEN_REVATEPOCH = 5; +}; + +#endif + + diff --git a/main.cpp b/main.cpp index c91bc2a..0683211 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include "Julian.h" +#include "Tle.h" #include