forked from nrfconnect/sdk-zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimeutil.c
190 lines (158 loc) · 4.59 KB
/
timeutil.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
* Copyright (c) 2019 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* The time_days_from_civil function is derived directly from public
* domain content written by Howard Hinnant and available at:
* http://howardhinnant.github.io/date_algorithms.html#days_from_civil
*/
#include <zephyr/types.h>
#include <errno.h>
#include <stddef.h>
#include <stdbool.h>
#include <zephyr/sys/timeutil.h>
/** Convert a civil (proleptic Gregorian) date to days relative to
* 1970-01-01.
*
* @param y the calendar year
* @param m the calendar month, in the range [1, 12]
* @param d the day of the month, in the range [1, last_day_of_month(y, m)]
*
* @return the signed number of days between the specified day and
* 1970-01-01
*
* @see http://howardhinnant.github.io/date_algorithms.html#days_from_civil
*/
static int64_t time_days_from_civil(int64_t y,
unsigned int m,
unsigned int d)
{
y -= m <= 2;
int64_t era = ((y >= 0) ? y : (y - 399)) / 400;
unsigned int yoe = y - era * 400;
unsigned int doy = (153U * (m + ((m > 2) ? -3 : 9)) + 2U) / 5U + d;
unsigned int doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
return era * 146097 + (time_t)doe - 719468;
}
int64_t timeutil_timegm64(const struct tm *tm)
{
int64_t y = TIME_UTILS_BASE_YEAR + (int64_t)tm->tm_year;
unsigned int m = tm->tm_mon + 1;
unsigned int d = tm->tm_mday - 1;
int64_t ndays = time_days_from_civil(y, m, d);
int64_t time = tm->tm_sec;
time += 60LL * (tm->tm_min + 60LL * tm->tm_hour);
time += 86400LL * ndays;
return time;
}
time_t timeutil_timegm(const struct tm *tm)
{
int64_t time = timeutil_timegm64(tm);
time_t rv = (time_t)time;
errno = 0;
if ((sizeof(rv) == sizeof(int32_t))
&& ((time < (int64_t)INT32_MIN)
|| (time > (int64_t)INT32_MAX))) {
errno = ERANGE;
rv = -1;
}
return rv;
}
int timeutil_sync_state_update(struct timeutil_sync_state *tsp,
const struct timeutil_sync_instant *inst)
{
int rv = -EINVAL;
if (((tsp->base.ref == 0) && (inst->ref > 0))
|| ((inst->ref > tsp->base.ref)
&& (inst->local > tsp->base.local))) {
if (tsp->base.ref == 0) {
tsp->base = *inst;
tsp->latest = (struct timeutil_sync_instant){};
tsp->skew = 1.0f;
rv = 0;
} else {
tsp->latest = *inst;
rv = 1;
}
}
return rv;
}
int timeutil_sync_state_set_skew(struct timeutil_sync_state *tsp, float skew,
const struct timeutil_sync_instant *base)
{
int rv = -EINVAL;
if (skew > 0) {
tsp->skew = skew;
if (base != NULL) {
tsp->base = *base;
tsp->latest = (struct timeutil_sync_instant){};
}
rv = 0;
}
return rv;
}
float timeutil_sync_estimate_skew(const struct timeutil_sync_state *tsp)
{
float rv = 0;
if ((tsp->base.ref != 0) && (tsp->latest.ref != 0)
&& (tsp->latest.local > tsp->base.local)) {
const struct timeutil_sync_config *cfg = tsp->cfg;
double ref_delta = tsp->latest.ref - tsp->base.ref;
double local_delta = tsp->latest.local - tsp->base.local;
rv = ref_delta * cfg->local_Hz / local_delta / cfg->ref_Hz;
}
return rv;
}
int timeutil_sync_ref_from_local(const struct timeutil_sync_state *tsp,
uint64_t local, uint64_t *refp)
{
int rv = -EINVAL;
if ((tsp->skew > 0) && (tsp->base.ref > 0) && (refp != NULL)) {
const struct timeutil_sync_config *cfg = tsp->cfg;
int64_t local_delta = local - tsp->base.local;
/* (x * 1.0) != x for large values of x.
* Therefore only apply the multiplication if the skew is not one.
*/
if (tsp->skew != 1.0f) {
local_delta *= (double)tsp->skew;
}
int64_t ref_delta = local_delta * cfg->ref_Hz / cfg->local_Hz;
int64_t ref_abs = (int64_t)tsp->base.ref + ref_delta;
if (ref_abs < 0) {
rv = -ERANGE;
} else {
*refp = ref_abs;
rv = (tsp->skew != 1.0f) ? 1 : 0;
}
}
return rv;
}
int timeutil_sync_local_from_ref(const struct timeutil_sync_state *tsp,
uint64_t ref, int64_t *localp)
{
int rv = -EINVAL;
if ((tsp->skew > 0) && (tsp->base.ref > 0) && (localp != NULL)) {
const struct timeutil_sync_config *cfg = tsp->cfg;
int64_t ref_delta = (int64_t)(ref - tsp->base.ref);
/* (x / 1.0) != x for large values of x.
* Therefore only apply the division if the skew is not one.
*/
int64_t local_delta = (ref_delta * cfg->local_Hz) / cfg->ref_Hz;
if (tsp->skew != 1.0f) {
local_delta /= (double)tsp->skew;
}
int64_t local_abs = (int64_t)tsp->base.local
+ (int64_t)local_delta;
*localp = local_abs;
rv = (tsp->skew != 1.0f) ? 1 : 0;
}
return rv;
}
int32_t timeutil_sync_skew_to_ppb(float skew)
{
int64_t ppb64 = (int64_t)((1.0 - (double)skew) * 1E9);
int32_t ppb32 = (int32_t)ppb64;
return (ppb64 == ppb32) ? ppb32 : INT32_MIN;
}