Skip to content

Commit

Permalink
Refactor sampled orbit and orientation classes
Browse files Browse the repository at this point in the history
- Store sample times and rotations in separate vectors to reduce padding
- Do not permit out-of-order timestamps in sample files
- Do not permit empty sampled trajectories or rotations
- Pre-rotate the sampled trajectory positions and velocities
- Consolidate loader code

Deduplicate the loader code

Deduplicate the index code

Revise includes
  • Loading branch information
ajtribick committed Oct 28, 2023
1 parent d3c5346 commit a7ba3f0
Show file tree
Hide file tree
Showing 6 changed files with 697 additions and 692 deletions.
2 changes: 2 additions & 0 deletions src/celephem/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ set(CELEPHEM_SOURCES
precession.h
rotation.cpp
rotation.h
sampfile.cpp
sampfile.h
samporbit.cpp
samporbit.h
samporient.cpp
Expand Down
139 changes: 139 additions & 0 deletions src/celephem/sampfile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// sampfile.cpp
//
// Utility functions for sampled orbit and rotation files.
//
// Copyright (C) 2023, Celestia Development Team
//
// Extracted from samporbit.cpp/samporient.cpp
// Copyright (C) 2008, Celestia Development Team
// Initial implementation by Chris Laurel <[email protected]>
//
// 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.

#include "sampfile.h"

#include <algorithm>
#include <cctype>

#include <celutil/gettext.h>
#include <celutil/logger.h>

using celestia::util::GetLogger;

namespace celestia::ephem
{

namespace detail
{

bool
checkSampleOrdering(double tdb,
double& lastSampleTime,
bool& hasOutOfOrderSamples,
const fs::path& filename)
{
if (tdb > lastSampleTime)
{
lastSampleTime = tdb;
return true;
}

if (!hasOutOfOrderSamples)
{
GetLogger()->warn(_("Skipping out-of-order samples in {}."), filename);
hasOutOfOrderSamples = true;
}

return false;
}


bool
logIfNoSamples(bool hasSamples, const fs::path& filename)
{
if (!hasSamples)
GetLogger()->error(_("No samples found in sample file {}.\n"), filename);

return hasSamples;
}


void
logReadError(const fs::path& filename)
{
GetLogger()->error(_("Error reading sample file {}.\n"), filename);
}


void
logOpenAsciiFail(const fs::path& filename)
{
GetLogger()->error(_("Error opening ASCII sample file {}.\n"), filename);
}


void
logSkipCommentsFail(const fs::path& filename)
{
GetLogger()->error(_("Error finding data in ASCII sample file {}.\n"), filename);
}


// Scan past comments. A comment begins with the # character and ends
// with a newline. Return true if the stream state is good. The stream
// position will be at the first non-comment, non-whitespace character.
bool skipComments(std::istream& in)
{
bool inComment = false;
for (;;)
{
int c = in.get();
if (!in.good())
return false;

if (inComment)
{
if (c == '\n')
inComment = false;
}
else
{
if (c == '#')
{
inComment = true;
}
else if (!std::isspace(static_cast<unsigned char>(c)))
{
in.unget();
return in.good();
}
}
}
}

} // end namespace celestia::ephem::detail

// Do a binary search to find the samples that define the orientation
// at the current time. Cache the previous sample used and avoid
// the search if it covers the requested time.
std::uint32_t
GetSampleIndex(double jd,
std::uint32_t& lastSample,
celestia::util::array_view<double> sampleTimes)
{
std::uint32_t n = lastSample;
const auto maxSample = static_cast<std::uint32_t>(sampleTimes.size());
if (n < 1 || n >= maxSample || jd < sampleTimes[n - 1] || jd > sampleTimes[n])
{
auto iter = std::lower_bound(sampleTimes.begin(), sampleTimes.end(), jd);
n = static_cast<std::uint32_t>(iter - sampleTimes.begin());
lastSample = n;
}

return n;
}

} // end namespace celestia::ephem
109 changes: 109 additions & 0 deletions src/celephem/sampfile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// sampfile.h
//
// Utility functions for sampled orbit and rotation files.
//
// Copyright (C) 2023, Celestia Development Team
//
// Extracted from samporbit.h/samporient.h
// Copyright (C) 2008, Celestia Development Team
// Initial implementation by Chris Laurel <[email protected]>
//
// 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.

#include <cstdint>
#include <fstream>
#include <istream>
#include <limits>
#include <utility>
#include <vector>

#include <celcompat/filesystem.h>
#include <celutil/array_view.h>

namespace celestia::ephem
{

namespace detail
{

bool checkSampleOrdering(double tdb,
double& lastSampleTime,
bool& hasOutOfOrderSamples,
const fs::path& filename);

bool logIfNoSamples(bool, const fs::path&);
void logReadError(const fs::path&);
void logOpenAsciiFail(const fs::path&);
void logSkipCommentsFail(const fs::path&);

bool skipComments(std::istream& in);

}


std::uint32_t GetSampleIndex(double jd,
std::uint32_t& lastSample,
celestia::util::array_view<double> sampleTimes);


template<typename T, typename F>
bool
LoadSamples(std::istream& in,
const fs::path& filename,
std::vector<double>& sampleTimes,
std::vector<T>& samples,
F readSample)
{
double lastSampleTime = -std::numeric_limits<double>::infinity();
bool hasOutOfOrderSamples = false;
for (;;)
{
double tdb;
T sample;
if (!readSample(in, tdb, sample))
{
if (in.eof())
return detail::logIfNoSamples(!sampleTimes.empty(), filename);

detail::logReadError(filename);
return false;
}

// Skip samples with duplicate or out-of-order times; such trajectories
// are invalid, but are unfortunately used in some existing add-ons.
if (!detail::checkSampleOrdering(tdb, lastSampleTime, hasOutOfOrderSamples, filename))
continue;

sampleTimes.push_back(tdb);
samples.push_back(std::move(sample));
}
}


template<typename T, typename F>
bool
LoadAsciiSamples(const fs::path& filename,
std::vector<double>& sampleTimes,
std::vector<T>& samples,
F readSample)
{
std::ifstream in(filename);
if (!in.good())
{
detail::logOpenAsciiFail(filename);
return false;
}

if (!detail::skipComments(in))
{
detail::logSkipCommentsFail(filename);
return false;
}

return LoadSamples(in, filename, sampleTimes, samples, readSample);
}

} // end namespace celestia::ephem
Loading

0 comments on commit a7ba3f0

Please sign in to comment.