ProtoCore v0.0.1
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
time_source.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file time_source.cpp
6 * @brief Multi-source time fallback matrix - implementation.
7 *
8 * A fixed BSS table of sources queried in ascending priority. See time_source.h.
9 */
10
11#include "time_source.h"
12#include "shared_primitives/http_date.h" // pc_http_date() - the shared IMF-fixdate formatter
13
14#if PC_ENABLE_TIME_SOURCE
15
16namespace
17{
18struct Src
19{
20 const char *name;
21 TimeSourceFn fn;
22 uint8_t priority;
23 bool used;
24};
25
26// All time-source state, owned by one instance (internal linkage): the priority-ordered
27// source table and the last-selected source name, grouped so it is one named owner.
28struct TimeSourceCtx
29{
30 Src sources[PC_TIME_SOURCE_MAX];
31 const char *active = nullptr;
32};
33TimeSourceCtx s_ts;
34} // namespace
35
36bool pc_time_source_add(const char *name, uint8_t priority, TimeSourceFn fn)
37{
38 if (!fn)
39 {
40 return false;
41 }
42 for (int i = 0; i < PC_TIME_SOURCE_MAX; i++)
43 {
44 if (!s_ts.sources[i].used)
45 {
46 s_ts.sources[i].name = name;
47 s_ts.sources[i].fn = fn;
48 s_ts.sources[i].priority = priority;
49 s_ts.sources[i].used = true;
50 return true;
51 }
52 }
53 return false; // table full
54}
55
56uint32_t pc_time_now(void)
57{
58 s_ts.active = nullptr;
59
60 // Query sources in ascending priority (lowest value first); stop at the first
61 // that returns a nonzero epoch. A selection scan avoids sorting and, more
62 // importantly, does not invoke lower-priority callbacks once a higher-priority
63 // source has answered (reading an RTC / GPS can be costly).
64 uint32_t queried = 0; // bitmask of sources already tried
65 for (int pass = 0; pass < PC_TIME_SOURCE_MAX; pass++)
66 {
67 int sel = -1;
68 for (int i = 0; i < PC_TIME_SOURCE_MAX; i++)
69 {
70 if (!s_ts.sources[i].used || (queried & (1u << i)))
71 {
72 continue;
73 }
74 if (sel < 0 || s_ts.sources[i].priority < s_ts.sources[sel].priority)
75 {
76 sel = i;
77 }
78 }
79 if (sel < 0)
80 {
81 break; // no more sources
82 }
83
84 queried |= (1u << sel);
85 uint32_t epoch = s_ts.sources[sel].fn();
86 if (epoch != 0)
87 {
88 s_ts.active = s_ts.sources[sel].name;
89 return epoch;
90 }
91 }
92 return 0;
93}
94
95const char *pc_time_source_active(void)
96{
97 return s_ts.active;
98}
99
100void pc_time_source_reset(void)
101{
102 for (int i = 0; i < PC_TIME_SOURCE_MAX; i++)
103 {
104 s_ts.sources[i] = Src{};
105 }
106 s_ts.active = nullptr;
107}
108
109#else // PC_ENABLE_TIME_SOURCE == 0 -> no-op stubs
110
111bool pc_time_source_add(const char *, uint8_t, TimeSourceFn)
112{
113 return false;
114}
115uint32_t pc_time_now(void)
116{
117 return 0;
118}
119const char *pc_time_source_active(void)
120{
121 return nullptr;
122}
124{
125}
126
127#endif // PC_ENABLE_TIME_SOURCE
128
129// The current best time (pc_time_now, any registered NTP / GPS / RTC / ... source) as an RFC 7231
130// IMF-fixdate. Defined unconditionally: with the registry disabled pc_time_now() is 0, so this
131// returns 0 (no Date). Lets the HTTP Date header draw from whatever time source is enabled.
132size_t pc_time_http_date(char *out, size_t out_cap)
133{
134 return pc_http_date((time_t)pc_time_now(), out, out_cap);
135}
Render a Unix epoch as an RFC 7231 IMF-fixdate.
uint8_t pc_http_date(time_t epoch, char *out, uint32_t out_cap)
Write epoch into out as an IMF-fixdate in GMT.
Definition http_date.h:41
#define PC_TIME_SOURCE_MAX
Maximum registered time sources (PC_ENABLE_TIME_SOURCE).
bool pc_time_source_add(const char *, uint8_t, TimeSourceFn)
Register a time source.
uint32_t pc_time_now(void)
Current best time.
void pc_time_source_reset(void)
Clear all registered sources.
const char * pc_time_source_active(void)
Name of the source that satisfied the last pc_time_now(), or nullptr.
size_t pc_time_http_date(char *out, size_t out_cap)
The current best time (pc_time_now, any registered NTP / GPS / RTC / ... source) formatted as an RFC ...
Multi-source time fallback matrix (PC_ENABLE_TIME_SOURCE).
uint32_t(* TimeSourceFn)(void)
A time source: returns the current Unix epoch seconds for this source, or 0 if it currently has no va...
Definition time_source.h:36