Skip to content

Commit

Permalink
Add OMMDateTime object and UTs
Browse files Browse the repository at this point in the history
  • Loading branch information
A-j-K committed Jul 10, 2023
1 parent 07972ba commit a954bab
Show file tree
Hide file tree
Showing 10 changed files with 388 additions and 90 deletions.
6 changes: 4 additions & 2 deletions plugins/Satellites/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ SET(Satellites_SRCS
gsatellite/SGP4.h
gsatellite/SGP4.cpp

omm.hpp
omm.cpp
OMM.hpp
OMM.cpp
OMMDateTime.hpp
OMMDateTime.cpp
gSatWrapper.hpp
gSatWrapper.cpp
Satellite.hpp
Expand Down
97 changes: 97 additions & 0 deletions plugins/Satellites/src/OMMDateTime.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2023 Andy Kirkham
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
*/

#include <QChar>
#include <QDate>
#include <QDebug>

#include "OMMDateTime.hpp"

OMMDateTime::OMMDateTime()
{}

OMMDateTime::~OMMDateTime()
{}

OMMDateTime::OMMDateTime(QString & s, Type t)
{
switch(t) {
case STR_TLE:
ctorTle(s);
break;
case STR_ISO8601:
ctorISO(s);
break;
default:
break;
}
}

// From SGP4.cpp
static void jday_SGP4(int year, int mon, int day, int hr, int minute, double sec, double & jd, double & jdFrac)
{
jd = 367.0 * year - floor((7 * (year + floor((mon + 9) / 12.0))) * 0.25) + floor(275 * mon / 9.0) + day +
1721013.5; // use - 678987.0 to go to mjd directly
jdFrac = (sec + minute * 60.0 + hr * 3600.0) / 86400.0;
if (fabs(jdFrac) > 1.0) {
double dtt = floor(jdFrac);
jd = jd + dtt;
jdFrac = jdFrac - dtt;
}
}

void OMMDateTime::ctorTle(const QString & s)
{
int year = s.mid(0, 2).toInt();
double day = s.mid(2).toDouble();
int whole_day = std::floor(day);
double frac_day = day - whole_day;

// Create a QDate.
year += (year < 57) ? 2000 : 1900;
QDate d(year, 1, 1);
d = d.addDays(whole_day - 1); // Minus 1 because we start on 1st Jan.

// Create the time.
double seconds = (24 * 60 * 60) * frac_day;
int whole_hours = std::floor(seconds / 3600);
seconds -= whole_hours * 3600;
int whole_mins = std::floor(seconds / 60);
seconds -= whole_mins * 60;

jday_SGP4(d.year(), d.month(), d.day(), whole_hours, whole_mins, seconds, m_epoch_jd, m_epoch_jd_frac);
}

void OMMDateTime::ctorISO(const QString & s)
{
QDateTime d = QDateTime::fromString(s, Qt::ISODate);
int year = d.date().year();
int mon = d.date().month();
int day = d.date().day();
int hour = d.time().hour();
int min = d.time().minute();
double sec = d.time().second();
auto decimal = s.indexOf(QChar('.'));
if(decimal > 0) {
auto frac_s = s.mid(decimal);
double frac_d = frac_s.toDouble();
sec += frac_d;
}
jday_SGP4(year, mon, day, hour, min, sec, m_epoch_jd, m_epoch_jd_frac);
}

65 changes: 65 additions & 0 deletions plugins/Satellites/src/OMMDateTime.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Stellarium
* Copyright (C) 2023 Andy Kirkham
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
*/

/*
* The standard QDateTime type only stores to the millisecond but
* satellite propargation models use to microsecond timimg.
* For Stellarium display purposes millisecond accuracy is
* probably good enough. However, the Unit Tests source data
* expectations from common SGP4 models and therefore fail to
* agree to routines that do not account for microsecond timing.
* This class therefore is to allow for us timings.
*
* Epoch times are stored as JD and JDF.
*/

#ifndef SATELLITES_OMMDATETIME_HPP
#define SATELLITES_OMMDATETIME_HPP

#include <QString>
#include <QDateTime>

class OMMDateTime
{
public:
enum Type {
STR_TLE,
STR_ISO8601
};

OMMDateTime();
~OMMDateTime();

OMMDateTime(QString& s, Type t = STR_TLE);

double getJulianDay() { return m_epoch_jd; }

double getJulianFrac() { return m_epoch_jd_frac; }

double getJulian() { return m_epoch_jd + m_epoch_jd_frac; }

private:
void ctorTle(const QString & s);
void ctorISO(const QString & s);

double m_epoch_jd{};
double m_epoch_jd_frac{};
};

#endif
138 changes: 80 additions & 58 deletions plugins/Satellites/src/omm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,47 @@
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
*/

#include <cmath>

#include <QPair>
#include <QDebug>

#include "omm.hpp"
#include "OMM.hpp"

namespace PluginSatellites {

omm::omm()
OMM::OMM()
{
m_source_type = SourceType::Invalid;
}

omm::~omm()
OMM::~OMM()
{}

omm::omm(QXmlStreamReader& r)
OMM::OMM(QXmlStreamReader& r)
{
m_source_type = SourceType::Invalid;
setFromXML(r);
}

omm::omm(QString & l1, QString & l2)
OMM::OMM(QString & l1, QString & l2)
{
m_source_type = SourceType::LegacyTle;
m_line1 = l1;
m_line2 = l2;
processTleLegacy();
}

omm::omm(QString& l0, QString& l1, QString& l2)
OMM::OMM(QString& l0, QString& l1, QString& l2)
{
m_source_type = SourceType::LegacyTle;
m_line0 = l0;
m_line1 = l1;
m_line2 = l2;
processTleLegacy();
}

bool omm::hasValidLegacyTleData()
bool OMM::hasValidLegacyTleData()
{
if(m_source_type == SourceType::LegacyTle) {
if(m_line1.startsWith('1') && m_line2.startsWith('2')) {
Expand All @@ -61,28 +66,30 @@ bool omm::hasValidLegacyTleData()
return false;
}

bool omm::setFromXML(QXmlStreamReader & r)
bool OMM::setFromXML(QXmlStreamReader & r)
{
if (r.name().toString().toLower() == "omm") {
bool collectChars = false;
QString savedTag;
QString savedVal;
m_source_type = SourceType::Xml;
r.readNext(); // Advance past starting <omm> tag.
r.readNext(); // Advance past starting <OMM> tag.
while (!r.hasError() && !r.atEnd()) {
QString tag = r.name().toString();
if (tag.toLower() == "omm") {
// Detected </omm> closing tag.
// Detected </OMM> closing tag.
return true;
}
if (r.isStartElement()) {
collectChars = true;
savedTag = tag.toUpper();
savedVal = "";
} else if (collectChars) {
}
else if (collectChars) {
if (r.isCharacters()) {
savedVal += r.text();
} else if (r.isEndElement()) {
}
else if (r.isEndElement()) {
processXmlElement(savedTag, savedVal);
collectChars = false;
savedVal = "";
Expand All @@ -99,56 +106,71 @@ bool omm::setFromXML(QXmlStreamReader & r)
return false;
}

void omm::processXmlElement(const QString & tag, const QString & val)
void OMM::processXmlElement(const QString & tag, const QString & val)
{
if (tag == "OBJECT_NAME") {
m_object_name = val;
}
else if (tag == "OBJECT_ID") {
m_object_id = val;
}
else if (tag == "EPOCH") {
if (tag == "EPOCH") {
m_epoch_str = val;
m_epoch = QDateTime::fromString(val, "yyyy-MM-ddThh:mm:ss.zzzzzz");
}
else if (tag == "MEAN_MOTION") {
m_mean_motion = val.toDouble();
}
else if (tag == "ECCENTRICITY") {
m_eccentricity = val.toDouble();
}
else if (tag == "INCLINATION") {
m_inclination = val.toDouble();
}
else if (tag == "RA_OF_ASC_NODE") {
m_ascending_node = val.toDouble();
}
else if (tag == "ARG_OF_PERICENTER") {
m_argument_perigee = val.toDouble();
}
else if (tag == "MEAN_ANOMALY") {
m_mean_anomoly = val.toDouble();
}
else if (tag == "CLASSIFICATION_TYPE") {
m_classification = val.at(0).toUpper();
}
else if (tag == "NORAD_CAT_ID") {
m_norad_cat_id = val.toInt();
}
else if (tag == "ELEMENT_SET_NO") {
m_element_set_no = val.toInt();
}
else if (tag == "REV_AT_EPOCH") {
m_rev_at_epoch = val.toInt();
}
else if (tag == "BSTAR") {
m_bstar = val.toDouble();
}
else if (tag == "MEAN_MOTION_DOT") {
m_mean_motion_dot = val.toDouble();
}
else if (tag == "MEAN_MOTION_DDOT") {
m_mean_motion_ddot = val.toDouble();
else if (tag == "OBJECT_NAME") m_object_name = val;
else if (tag == "OBJECT_ID") m_object_id = val;
else if (tag == "MEAN_MOTION") m_mean_motion = val.toDouble();
else if (tag == "ECCENTRICITY") m_eccentricity = val.toDouble();
else if (tag == "INCLINATION") m_inclination = val.toDouble();
else if (tag == "RA_OF_ASC_NODE") m_ascending_node = val.toDouble();
else if (tag == "ARG_OF_PERICENTER") m_argument_perigee = val.toDouble();
else if (tag == "MEAN_ANOMALY") m_mean_anomoly = val.toDouble();
else if (tag == "CLASSIFICATION_TYPE") m_classification = val.at(0).toUpper();
else if (tag == "NORAD_CAT_ID") m_norad_cat_id = val.toInt();
else if (tag == "ELEMENT_SET_NO") m_element_set_no = val.toInt();
else if (tag == "REV_AT_EPOCH") m_rev_at_epoch = val.toInt();
else if (tag == "BSTAR") m_bstar = val.toDouble();
else if (tag == "MEAN_MOTION_DOT") m_mean_motion_dot = val.toDouble();
else if (tag == "MEAN_MOTION_DDOT") m_mean_motion_ddot = val.toDouble();
}

// Everything below here is for extracting the data from the two TLE lines.

// J F M A M J J A S O N D
static int month_lens[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// TLE Line1 field positions and lengths.
static QPair<int, int> NORAD_CAT_ID(2,5);
static QPair<int, int> CLASSIFICATION_TYPE(7,1);
static QPair<int, int> OBJECT_ID(9, 8);
static QPair<int, int> EPOCH_YEAR(18, 2);
static QPair<int, int> EPOCH_DAY(20, 12);
static QPair<int, int> MEAN_MOTION_DOT(33, 10);
static QPair<int, int> MEAN_MOTION_DDOT(44, 8);
static QPair<int, int> BSTAR(53, 8);

// TLE Line2 field positions and lengths.
static QPair<int, int> INCLINATION(8, 8);
static QPair<int, int> RA_OF_ASC_NODE(17, 8);
static QPair<int, int> ECCENTRICITY(26, 7);
static QPair<int, int> ARG_OF_PERICENTER(34, 8);
static QPair<int, int> MEAN_ANOMALY(43, 8);
static QPair<int, int> MEAN_MOTION(52, 11);
static QPair<int, int> REV_AT_EPOCH(63, 5);

void OMM::processTleLegacy(void)
{
if (m_line1.at(0) == '1') {
m_norad_cat_id = m_line1.mid(NORAD_CAT_ID.first, NORAD_CAT_ID.second).toInt();
m_classification = m_line1.at(CLASSIFICATION_TYPE.first);
m_object_id = m_line1.mid(OBJECT_ID.first, OBJECT_ID.second).trimmed();
int epoch_year = m_line1.mid(EPOCH_YEAR.first, EPOCH_YEAR.second).toInt();
if (epoch_year < 57) epoch_year += 2000;
else epoch_year += 1900;
QDate year = QDate(epoch_year, 1, 1);
double epoch_day = m_line1.mid(EPOCH_DAY.first, EPOCH_DAY.second).toDouble();
int day = std::floor(epoch_day);

// 18-31 Epoch. Element Set Epoch (UTC) *Note: spaces are acceptable in columns 20 & 21
m_mean_motion_dot = m_line1.mid(33, 10).toDouble();
QString dec(".");
dec.append(m_line1.mid(44, 5));
m_mean_motion_ddot = dec.toDouble();
}
}

Expand Down
Loading

0 comments on commit a954bab

Please sign in to comment.