FOUND Coverage Report


src/
File: common/time/time.cpp
Date: 2026-03-24 21:41:51
Lines:
52/52
100.0%
Functions:
8/8
100.0%
Branches:
6/6
100.0%

Line Branch Exec Source
1 #include "common/time/time.hpp"
2
3 #include <chrono>
4 #include <cstdint>
5 #include <ctime>
6
7 #include "common/decimal.hpp"
8
9 // NOTE: We use a .cpp file instead of just putting these into
10 // the header file as inlined functions since these functions
11 // can get more complex and can be modified with different
12 // spacecraft requirements. For instance, the functions that
13 // obtain UTC and UT1 time can be modified to use an onboard
14 // atomic clock or GPS reciever.
15
16 // NOTE: To use approximations for getting the Julian Datetime
17 // and GMST for the current time, define the macro FAST
18 // #define FAST
19
20 namespace found {
21
22 6 DateTime getUTCTime() {
23 // Use chrono for nanosecond precision
24 6 std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
25 6 std::chrono::system_clock::duration duration = now.time_since_epoch();
26 6 std::chrono::nanoseconds nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(duration);
27 6 uint64_t epochs_ns = static_cast<uint64_t>(nanos.count());
28
29 // Get broken-down time for year/month/day/etc.
30 6 std::time_t now_t = std::chrono::system_clock::to_time_t(now);
31 6 std::tm* utc_time = std::gmtime(&now_t);
32
33 // Calculate nanoseconds within the current second
34
1/1
✓ Branch 2 taken 6 times.
6 std::chrono::seconds secs = std::chrono::duration_cast<std::chrono::seconds>(duration);
35 12 uint64_t nanosecond = static_cast<uint64_t>((nanos -
36
1/1
✓ Branch 2 taken 6 times.
12 std::chrono::duration_cast<std::chrono::nanoseconds>(secs)).count());
37
38 return {
39 epochs_ns,
40 6 static_cast<uint64_t>(utc_time->tm_year + 1900), // tm_year is years since 1900
41 6 static_cast<uint64_t>(utc_time->tm_mon + 1), // tm_mon is months since January (0-11)
42 6 static_cast<uint64_t>(utc_time->tm_mday), // tm_mday is day of the month (1-31)
43 6 static_cast<uint64_t>(utc_time->tm_hour), // tm_hour is hours since midnight (0-23)
44 6 static_cast<uint64_t>(utc_time->tm_min), // tm_min is minutes after the hour (0-59)
45 6 static_cast<uint64_t>(utc_time->tm_sec), // tm_sec is seconds after the minute (0-60)
46 nanosecond // nanoseconds within current second
47 12 };
48 }
49
50 5 DateTime getUT1Time() {
51 // TODO: For simplicity, we return an approximation of UT1,
52 // whose formula is UT1 = UTC + Delta UT1. This should be
53 // replaced with either a table lookup or some curve function.
54 //
55 // Based on https://maia.usno.navy.mil/ser7/ser7.dat, which
56 // provides Delta UT1 values from 2025 to 2026, the average is
57 // 0.087497 seconds, so we use that as a rough approximation.
58 5 DateTime now = getUTCTime();
59 // Add AVG_DELTA_UT1 in nanoseconds
60 5 now.epochs += AVG_DELTA_UT1_NS;
61 5 now.nanosecond = now.epochs % NS_PER_SEC;
62 5 return now;
63 }
64
65 10 decimal getJulianDateTime(DateTime &time) {
66 // Purportedly, the Julian date is also
67 // time.epochs / NS_PER_DAY + JULIAN_UNIX_EPOCH, but
68 // this is apparently just an approximation.
69
70 // The below formula is from https://aa.usno.navy.mil/faq/JD_formula
71 10 int64_t A = 367 * time.year;
72 10 int64_t B = (7 * (time.year + (time.month + 9) / 12)) / 4;
73 10 int64_t C = (275 * time.month) / 9;
74 10 decimal D = time.day + 1721013.5;
75 10 decimal julianDate = A - B + C + D;
76
77 10 return julianDate + time.hour / HOURS_PER_DAY +
78 10 time.minute / MIN_PER_DAY + time.second / SEC_PER_DAY +
79 20 time.nanosecond / NS_PER_DAY -
80
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 1 times.
10 DECIMAL(0.5) * (100 * time.year + time.month > JD_CONSTANT_FEB_1990 ? 1 : -1) +
81 10 DECIMAL(0.5);
82 }
83
84 1 decimal getCurrentJulianDateTime() {
85 #ifdef FAST
86 // If FAST is defined, we use the approximation with nanosecond precision
87 std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
88 std::chrono::nanoseconds nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch());
89 return getJulianDateTime(static_cast<uint64_t>(nanos.count()));
90 #else
91
1/1
✓ Branch 2 taken 1 times.
1 DateTime time = getUT1Time();
92 2 return getJulianDateTime(time);
93 #endif
94 }
95
96 2 decimal getJulianDateTime(uint64_t epochs) {
97 // epochs is in nanoseconds, divide by nanoseconds per day
98 2 return epochs / NS_PER_DAY + JULIAN_UNIX_EPOCH;
99 }
100
101 3 decimal getGreenwichMeanSiderealTime(DateTime &time) {
102 // Purportedly, the Greenwich Mean Sidereal Time (GMST)
103 // is also 15(18.697374558 + 24.06570982441908 * (JDT - J2000_JULIAN_DATE))
104 // in degrees
105
106 // The below formula is from http://tiny.cc/4wal001
107 3 decimal JDT = getJulianDateTime(time);
108 3 decimal D_tt = JDT - J2000_JULIAN_DATE; // Julian date - J2000.0
109 3 decimal t = D_tt / DAYS_PER_JULIAN_CENTURY; // Julian centuries since J2000.0
110
111 3 return DECIMAL(280.46061837) +
112 3 DECIMAL(360.98564736629) * D_tt +
113 3 (DECIMAL(0.000387933) * t * t) -
114 3 (t * t * t / DECIMAL(38710000.0));
115 }
116
117 1 decimal getCurrentGreenwichMeanSiderealTime() {
118 #ifdef FAST
119 // If FAST is defined, we use the approximation with nanosecond precision
120 std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
121 std::chrono::nanoseconds nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(now.time_since_epoch());
122 return getGreenwichMeanSiderealTime(static_cast<uint64_t>(nanos.count()));
123 #else
124
1/1
✓ Branch 2 taken 1 times.
1 DateTime time = getUT1Time();
125 2 return getGreenwichMeanSiderealTime(time);
126 #endif
127 }
128
129 1 decimal getGreenwichMeanSiderealTime(uint64_t epochs) {
130 1 return 15 * (DECIMAL(18.697374558) + DECIMAL(24.06570982441908) *
131 1 (getJulianDateTime(epochs) - J2000_JULIAN_DATE));
132 }
133
134 } // namespace found
135