Add new API to EclipseFinder

pull/286/head
Hleb Valoshka 2019-05-05 20:33:29 +03:00
parent 1af23318fd
commit ccb39d3342
2 changed files with 215 additions and 6 deletions

View File

@ -24,7 +24,11 @@ using namespace Eigen;
using namespace std;
constexpr const int EclipseObjectMask = Body::Planet |
Body::Moon |
Body::MinorMoon |
Body::DwarfPlanet |
Body::Asteroid;
Eclipse::Eclipse(int Y, int M, int D)
{
@ -44,6 +48,13 @@ Eclipse::~Eclipse()
// TODO: share this constant and function with render.cpp
static const float MinRelativeOccluderRadius = 0.005f;
EclipseFinder::EclipseFinder(Body* _body,
EclipseFinderWatcher* _watcher) :
body(_body),
watcher(_watcher)
{
};
bool EclipseFinder::testEclipse(const Body& receiver, const Body& caster,
double now) const
{
@ -227,3 +238,172 @@ int EclipseFinder::CalculateEclipses() // XXX: this function is very fragile and
return 0;
}
void EclipseFinder::findEclipses(double startDate,
double endDate,
int eclipseTypeMask,
vector<Eclipse>& eclipses)
{
PlanetarySystem* satellites = body->getSatellites();
// See if there's anything that could test
if (satellites == nullptr)
return;
// For each body, we'll need to store the time when the last eclipse ended
vector<double> previousEclipseEndTimes;
// Make a list of satellites that we'll actually test for eclipses; ignore
// spacecraft and very small objects.
vector<Body*> testBodies;
for (int i = 0; i < satellites->getSystemSize(); i++)
{
Body* obj = satellites->getBody(i);
if ((obj->getClassification() & EclipseObjectMask) != 0 &&
obj->getRadius() >= body->getRadius() * MinRelativeOccluderRadius)
{
testBodies.push_back(obj);
previousEclipseEndTimes.push_back(startDate - 1.0);
}
}
if (testBodies.empty())
return;
// TODO: Use a fixed step of one hour for now; we should use a binary
// search instead.
double searchStep = 1.0 / 24.0; // one hour
// Precision of eclipse duration calculation
double durationPrecision = 1.0 / (24.0 * 360.0); // ten seconds
for (double t = startDate; t <= endDate; t += searchStep)
{
if (watcher != nullptr)
{
if (watcher->eclipseFinderProgressUpdate(t) == EclipseFinderWatcher::AbortOperation)
return;
}
for (unsigned int i = 0; i < testBodies.size(); i++)
{
Body* occulter = nullptr;
Body* receiver = nullptr;
if (eclipseTypeMask == Eclipse::Solar)
{
occulter = testBodies[i];
receiver = body;
}
else
{
occulter = body;
receiver = testBodies[i];
}
// Only test for an eclipse if we're not in the middle of
// of previous one.
if (t > previousEclipseEndTimes[i])
{
if (eclipseTypeMask & Eclipse::Solar)
{
occulter = testBodies[i];
receiver = body;
if (testEclipse(*receiver, *occulter, t))
{
Eclipse eclipse;
eclipse.startTime = findEclipseStart(*receiver, *occulter, t, searchStep, durationPrecision);
eclipse.endTime = findEclipseEnd(*receiver, *occulter, t, searchStep, durationPrecision);
eclipse.receiver = receiver;
eclipse.occulter = occulter;
eclipses.push_back(eclipse);
previousEclipseEndTimes[i] = eclipse.endTime;
}
}
if (eclipseTypeMask & Eclipse::Lunar)
{
occulter = body;
receiver = testBodies[i];
if (testEclipse(*receiver, *occulter, t))
{
Eclipse eclipse;
eclipse.startTime = findEclipseStart(*receiver, *occulter, t, searchStep, durationPrecision);
eclipse.endTime = findEclipseEnd(*receiver, *occulter, t, searchStep, durationPrecision);
eclipse.receiver = receiver;
eclipse.occulter = occulter;
eclipses.push_back(eclipse);
previousEclipseEndTimes[i] = eclipse.endTime;
}
}
}
}
}
}
// Given a time during an eclipse, find the start of the eclipse to
// a precision of minStep.
double EclipseFinder::findEclipseStart(const Body& receiver, const Body& occulter,
double now,
double startStep,
double minStep) const
{
double step = startStep / 2;
double t = now - step;
bool eclipsed = true;
// Perform a binary search to find the end of the eclipse
while (step > minStep)
{
eclipsed = testEclipse(receiver, occulter, t);
step *= 0.5;
if (eclipsed)
t -= step;
else
t += step;
}
// Always return a time when the receiver is /not/ in eclipse
if (eclipsed)
t -= step;
return t;
}
// Given a time during an eclipse, find the end of the eclipse to
// a precision of minStep.
double EclipseFinder::findEclipseEnd(const Body& receiver, const Body& occulter,
double now,
double startStep,
double minStep) const
{
// First do a coarse search to find the eclipse end to within the precision
// of startStep.
while (testEclipse(receiver, occulter, now + startStep))
now += startStep;
double step = startStep / 2;
double t = now + step;
bool eclipsed = true;
// Perform a binary search to find the end of the eclipse
while (step > minStep)
{
eclipsed = testEclipse(receiver, occulter, t);
step *= 0.5;
if (eclipsed)
t += step;
else
t -= step;
}
// Always return a time when the receiver is /not/ in eclipse
if (eclipsed)
t += step;
return t;
}

View File

@ -14,23 +14,29 @@
#define _ECLIPSEFINDER_H_
#include <vector>
#include "celestiacore.h"
class Eclipse
{
public:
public:
Eclipse() = default;
Eclipse(int Y, int M, int D);
Eclipse(double JD);
~Eclipse();
// values must be 2^n
enum Type {
Solar = 0,
Moon = 1
Solar = 0x01,
Moon = 0x02,
Lunar = 0x02
};
public:
public:
Body* body{ nullptr };
Body* occulter{ nullptr };
Body* receiver{ nullptr };
std::string planete;
std::string sattelite;
astro::Date* date{ nullptr };
@ -38,9 +44,22 @@ public:
double endTime{ 0.0 };
};
class EclipseFinderWatcher
{
public:
enum Status
{
ContinueOperation = 0,
AbortOperation = 1,
};
virtual Status eclipseFinderProgressUpdate(double t) = 0;
};
class EclipseFinder
{
public:
EclipseFinder(Body*, EclipseFinderWatcher* _watcher);
EclipseFinder(CelestiaCore* core,
const std::string& strPlaneteToFindOn_,
Eclipse::Type type_,
@ -53,6 +72,11 @@ class EclipseFinder
JDto(to),
toProcess(true) {};
void findEclipses(double startDate,
double endDate,
int eclipseTypeMask,
vector<Eclipse>& eclipses);
const std::vector<Eclipse>& getEclipses() { if (toProcess) CalculateEclipses(); return Eclipses_; };
private:
@ -65,10 +89,15 @@ class EclipseFinder
bool toProcess;
double findEclipseStart(const Body& recever, const Body& occulter, double now, double startStep, double minStep) const;
double findEclipseEnd(const Body& recever, const Body& occulter, double now, double startStep, double minStep) const;
int CalculateEclipses();
bool testEclipse(const Body& receiver, const Body& caster, double now) const;
double findEclipseSpan(const Body& receiver, const Body& caster, double now, double dt) const;
Body* body;
EclipseFinderWatcher* watcher;
};
#endif // _ECLIPSEFINDER_H_