Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 17 additions & 27 deletions include/tempoch/period.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
* class template `Period<T>`. The underlying storage is always
* `tempoch_period_mjd_t`; `TimeTraits<T>` handles conversion to/from the raw
* MJD doubles.
*
* `TimeTraits` specialisations for every `Time<S>` are provided automatically
* by `time_base.hpp`. A manual `CivilTime` specialisation is kept here for
* `Period<CivilTime>` (i.e. `UTCPeriod`).
*/

#include "qtty/qtty.hpp"
Expand All @@ -18,32 +22,18 @@
namespace tempoch {

// ============================================================================
// TimeTraits — connect each time type to the MJD-based FFI layer
// TimeTraits — CivilTime specialisation
// ============================================================================
// TimeTraits<Time<S>> for all scale-based types is already defined in
// time_base.hpp. We only need the CivilTime-specific one here.

/**
* @brief Conversion traits between a time type @p T and raw MJD doubles.
*
* Specialise this struct to make `Period<T>` work for a custom time type.
* Each specialisation must provide:
* - `static double to_mjd_value(const T&)`
* - `static T from_mjd_value(double)`
*/
template <typename T> struct TimeTraits;

template <> struct TimeTraits<MJD> {
static double to_mjd_value(const MJD &t) { return t.value(); }
static MJD from_mjd_value(double m) { return MJD(m); }
};

template <> struct TimeTraits<JulianDate> {
static double to_mjd_value(const JulianDate &t) { return t.to_mjd(); }
static JulianDate from_mjd_value(double m) { return MJD(m).to_jd(); }
};

template <> struct TimeTraits<UTC> {
static double to_mjd_value(const UTC &t) { return MJD::from_utc(t).value(); }
static UTC from_mjd_value(double m) { return MJD(m).to_utc(); }
template <> struct TimeTraits<CivilTime> {
static double to_mjd_value(const CivilTime &t) {
return TimeScaleTraits<MJDScale>::from_civil(t);
}
static CivilTime from_mjd_value(double m) {
return TimeScaleTraits<MJDScale>::to_civil(m);
}
};

// ============================================================================
Expand Down Expand Up @@ -128,8 +118,8 @@ template <typename T = MJD> class Period {
*/
template <typename TargetType = qtty::DayTag>
qtty::Quantity<typename qtty::ExtractTag<TargetType>::type> duration() const {
double days = tempoch_period_mjd_duration_days(m_inner);
return qtty::Quantity<qtty::DayTag>(days).template to<TargetType>();
auto qty = tempoch_period_mjd_duration_qty(m_inner);
return qtty::Quantity<qtty::DayTag>(qty.value).template to<TargetType>();
}

/**
Expand Down Expand Up @@ -162,7 +152,7 @@ template <typename T> Period(T, T) -> Period<T>;

using MJDPeriod = Period<MJD>; ///< Period expressed in Modified Julian Date.
using JDPeriod = Period<JulianDate>; ///< Period expressed in Julian Date.
using UTCPeriod = Period<UTC>; ///< Period expressed in UTC civil time.
using UTCPeriod = Period<CivilTime>; ///< Period expressed in UTC civil time.

// ============================================================================
// operator<<
Expand Down
205 changes: 205 additions & 0 deletions include/tempoch/scales.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
#pragma once

/**
* @file scales.hpp
* @brief Time-scale tag types and traits for the tempoch Time<S> template.
*
* Mirrors the Rust `tempoch_core::scales` module. Each tag is an empty struct
* that selects the FFI functions used by `Time<S>`.
*
* Adding a new scale requires:
* 1. Define a tag struct (e.g. `struct TTScale {};`).
* 2. Specialise `TimeScaleTraits<TTScale>` with the FFI calls.
* 3. Specialise `TimeConvertTraits<A,B>` for each supported conversion pair.
*/

#include "ffi_core.hpp" // tempoch_ffi.h + check_status

namespace tempoch {

// Forward declaration — defined in time_base.hpp.
struct CivilTime;

// ============================================================================
// Scale Tags
// ============================================================================

/// Julian Date (days since −4712‑01‑01T12:00 TT).
struct JDScale {};

/// Modified Julian Date (JD − 2 400 000.5).
struct MJDScale {};

/// UTC, internally stored as MJD days for arithmetic.
struct UTCScale {};

// Stubs for future FFI-backed scales — uncomment and specialise traits once
// the FFI exposes the conversion functions.
// struct TTScale {}; // Terrestrial Time
// struct TAIScale {}; // International Atomic Time
// struct TDBScale {}; // Barycentric Dynamical Time
// struct TCGScale {}; // Geocentric Coordinate Time
// struct TCBScale {}; // Barycentric Coordinate Time
// struct GPSScale {}; // GPS Time
// struct UTScale {}; // Universal Time (UT1)

// ============================================================================
// TimeScaleTraits — per-scale FFI dispatch
// ============================================================================

/**
* @brief Primary template — must be specialised for every supported scale.
*
* Required static members:
* - `const char* label()`
* - `double from_utc(const CivilTime&)` — civil time → raw days
* - `CivilTime to_utc(double days)` — raw days → civil time
* - `double add_days(double days, double delta)`
* - `double difference(double a, double b)` — a − b in days
*/
template <typename S> struct TimeScaleTraits {
static_assert(sizeof(S) == 0,
"TimeScaleTraits<S> must be specialised for this scale.");
};

// ── JDScale ─────────────────────────────────────────────────────────────────

template <> struct TimeScaleTraits<JDScale> {
static constexpr const char *label() { return "JD"; }

static double from_civil(const CivilTime &ct);
static CivilTime to_civil(double jd);

static double add_days(double jd, double delta) {
return tempoch_jd_add_days(jd, delta);
}

static double difference(double a, double b) {
return tempoch_jd_difference(a, b);
}

/// Difference as a typed `qtty_quantity_t` (Day unit).
static qtty_quantity_t difference_qty(double a, double b) {
return tempoch_jd_difference_qty(a, b);
}

/// Add a typed duration quantity and write result to @p out.
static void add_qty(double jd, qtty_quantity_t duration, double &out) {
check_status(tempoch_jd_add_qty(jd, duration, &out), "Time<JD>::add_qty");
}

/// J2000.0 epoch constant (JD 2 451 545.0).
static double j2000() { return tempoch_jd_j2000(); }

/// Julian centuries elapsed since J2000.
static double julian_centuries(double jd) {
return tempoch_jd_julian_centuries(jd);
}

/// Julian centuries since J2000 as a typed `qtty_quantity_t`
/// (JulianCentury unit).
static qtty_quantity_t julian_centuries_qty(double jd) {
return tempoch_jd_julian_centuries_qty(jd);
}
};

// ── MJDScale ────────────────────────────────────────────────────────────────

template <> struct TimeScaleTraits<MJDScale> {
static constexpr const char *label() { return "MJD"; }

static double from_civil(const CivilTime &ct);
static CivilTime to_civil(double mjd);

static double add_days(double mjd, double delta) {
return tempoch_mjd_add_days(mjd, delta);
}

static double difference(double a, double b) {
return tempoch_mjd_difference(a, b);
}

/// Difference as a typed `qtty_quantity_t` (Day unit).
static qtty_quantity_t difference_qty(double a, double b) {
return tempoch_mjd_difference_qty(a, b);
}

/// Add a typed duration quantity and write result to @p out.
static void add_qty(double mjd, qtty_quantity_t duration, double &out) {
check_status(tempoch_mjd_add_qty(mjd, duration, &out),
"Time<MJD>::add_qty");
}
};

// ── UTCScale (internally stored as MJD) ─────────────────────────────────────

template <> struct TimeScaleTraits<UTCScale> {
static constexpr const char *label() { return "UTC"; }

static double from_civil(const CivilTime &ct);
static CivilTime to_civil(double mjd);

static double add_days(double mjd, double delta) {
return tempoch_mjd_add_days(mjd, delta);
}

static double difference(double a, double b) {
return tempoch_mjd_difference(a, b);
}

/// Difference as a typed `qtty_quantity_t` (Day unit).
static qtty_quantity_t difference_qty(double a, double b) {
return TimeScaleTraits<MJDScale>::difference_qty(a, b);
}

/// Add a typed duration quantity and write result to @p out.
static void add_qty(double mjd, qtty_quantity_t duration, double &out) {
TimeScaleTraits<MJDScale>::add_qty(mjd, duration, out);
}
};

// ============================================================================
// TimeConvertTraits — cross-scale conversion
// ============================================================================

/**
* @brief Primary template — specialise for each supported A→B pair.
*
* Required: `static double convert(double src_days)`.
*/
template <typename From, typename To> struct TimeConvertTraits {
static_assert(sizeof(From) == 0,
"TimeConvertTraits<From,To> is not specialised for this pair.");
};

// ── JD ↔ MJD ────────────────────────────────────────────────────────────────

template <> struct TimeConvertTraits<JDScale, MJDScale> {
static double convert(double jd) { return tempoch_jd_to_mjd(jd); }
};

template <> struct TimeConvertTraits<MJDScale, JDScale> {
static double convert(double mjd) { return tempoch_mjd_to_jd(mjd); }
};

// ── JD ↔ UTC (UTC stored as MJD internally) ────────────────────────────────

template <> struct TimeConvertTraits<JDScale, UTCScale> {
static double convert(double jd) { return tempoch_jd_to_mjd(jd); }
};

template <> struct TimeConvertTraits<UTCScale, JDScale> {
static double convert(double mjd) { return tempoch_mjd_to_jd(mjd); }
};

// ── MJD ↔ UTC (identity — both stored as MJD) ──────────────────────────────

template <> struct TimeConvertTraits<MJDScale, UTCScale> {
static double convert(double mjd) { return mjd; }
};

template <> struct TimeConvertTraits<UTCScale, MJDScale> {
static double convert(double mjd) { return mjd; }
};

} // namespace tempoch
18 changes: 11 additions & 7 deletions include/tempoch/tempoch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,24 @@
* @brief Umbrella header for the tempoch C++ wrapper library.
*
* Include this single header to get the full tempoch C++ API:
* - `tempoch::UTC` — UTC date-time breakdown
* - `tempoch::JulianDate` — Julian Date wrapper
* - `tempoch::MJD` — Modified Julian Date wrapper
* - `tempoch::Period` — Time period [start, end] in MJD
*
* - `tempoch::Time<S>` — generic time-point template (core)
* - `tempoch::JulianDate` = `Time<JDScale>`
* - `tempoch::MJD` = `Time<MJDScale>`
* - `tempoch::CivilTime` — UTC civil date-time breakdown
* - `tempoch::UTC` — alias for `CivilTime`
* - `tempoch::Period<T>` — time period [start, end]
*
* @code
* #include <tempoch/tempoch.hpp>
*
* auto jd = tempoch::JulianDate::from_utc({2026, 7, 15, 22, 0, 0});
* auto mjd = tempoch::MJD::from_jd(jd);
* tempoch::Period night(60200.0, 60200.5);
* auto jd = tempoch::JulianDate::from_utc({2026, 7, 15, 22, 0, 0});
* auto mjd = jd.to<tempoch::MJDScale>(); // cross-scale conversion
* @endcode
*/

#include "ffi_core.hpp"
#include "period.hpp"
#include "scales.hpp"
#include "time.hpp"
#include "time_base.hpp"
Loading