Various fixes around Companion trainer mode (#7116)
[opentx.git] / radio / src / rtc.cpp
bloba82bbef688a294429b33f43dca61deb63f8dad1b
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include <limits.h>
22 #include "opentx.h"
24 extern void rtcdriver_settime(struct gtm * t);
26 #define LEAP_SECONDS_POSSIBLE 0
28 /* Shift A right by B bits portably, by dividing A by 2**B and
29 truncating towards minus infinity. A and B should be free of side
30 effects, and B should be in the range 0 <= B <= INT_BITS - 2, where
31 INT_BITS is the number of useful bits in an int. GNU code can
32 assume that INT_BITS is at least 32.
34 ISO C99 says that A >> B is implementation-defined if A < 0. Some
35 implementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shift
36 right in the usual way when A < 0, so SHR falls back on division if
37 ordinary A >> B doesn't seem to be the usual signed shift. */
38 #define SHR(a, b) (-1 >> 1 == -1 ? (a) >> (b) : (a) / (1 << (b)) - ((a) % (1 << (b)) < 0))
40 /* The extra casts in the following macros work around compiler bugs,
41 e.g., in Cray C 5.0.3.0. */
43 /* True if the arithmetic type T is an integer type. bool counts as
44 an integer. */
45 #define TYPE_IS_INTEGER(t) ((t) 1.5 == 1)
47 /* True if negative values of the signed integer type T use two's
48 complement, ones' complement, or signed magnitude representation,
49 respectively. Much GNU code assumes two's complement, but some
50 people like to be portable to all possible C hosts. */
51 #define TYPE_TWOS_COMPLEMENT(t) ((t) ~ (t) 0 == (t) -1)
52 #define TYPE_ONES_COMPLEMENT(t) ((t) ~ (t) 0 == 0)
53 #define TYPE_SIGNED_MAGNITUDE(t) ((t) ~ (t) 0 < (t) -1)
55 /* True if the arithmetic type T is signed. */
56 #define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
58 /* The maximum and minimum values for the integer type T. These
59 macros have undefined behavior if T is signed and has padding bits.
60 If this is a problem for you, please let us know how to fix it for
61 your host. */
62 #define TYPE_MINIMUM(t) \
63 ((t) (! TYPE_SIGNED (t) \
64 ? (t) 0 \
65 : TYPE_SIGNED_MAGNITUDE (t) \
66 ? ~ (t) 0 \
67 : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)))
68 #define TYPE_MAXIMUM(t) \
69 ((t) (! TYPE_SIGNED (t) \
70 ? (t) -1 \
71 : ~ (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))))
73 #ifndef TIME_T_MIN
74 # define TIME_T_MIN TYPE_MINIMUM (gtime_t)
75 #endif
76 #ifndef TIME_T_MAX
77 # define TIME_T_MAX TYPE_MAXIMUM (gtime_t)
78 #endif
79 #define TIME_T_MIDPOINT (SHR (TIME_T_MIN + TIME_T_MAX, 1) + 1)
83 static_assert(TYPE_IS_INTEGER(gtime_t), "gtime_t is not integer");
84 static_assert(TYPE_TWOS_COMPLEMENT(int), "twos complement arithmetic");
85 /* The code also assumes that signed integer overflow silently wraps
86 around, but this assumption can't be stated without causing a
87 diagnostic on some hosts. */
89 #define EPOCH_YEAR 1970
90 static_assert(TM_YEAR_BASE % 100 == 0, "base year is not a multiple of 100");
92 /* Return 1 if YEAR + TM_YEAR_BASE is a leap year. */
93 static inline int leapyear(long int year)
95 /* Don't add YEAR to TM_YEAR_BASE, as that might overflow.
96 Also, work even if YEAR is negative. */
97 return ((year & 3) == 0
98 && (year % 100 != 0
99 || ((year / 100) & 3) == (-(TM_YEAR_BASE / 100) & 3)));
102 const unsigned short int __mon_yday[2][13] = {
103 /* Normal years. */
104 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
105 /* Leap years. */
106 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
109 /* Compute the `struct tm' representation of *T,
110 offset OFFSET seconds east of UTC,
111 and store year, yday, mon, mday, wday, hour, min, sec into *TP.
112 Return nonzero if successful. */
113 int __offtime(const gtime_t * t, long int offset, struct gtm * tp)
115 long int days, rem, y;
116 const unsigned short int * ip;
118 days = *t / SECS_PER_DAY;
119 rem = *t % SECS_PER_DAY;
120 rem += offset;
121 while (rem < 0) {
122 rem += SECS_PER_DAY;
123 --days;
125 while (rem >= (long int)SECS_PER_DAY) {
126 rem -= SECS_PER_DAY;
127 ++days;
129 tp->tm_hour = rem / SECS_PER_HOUR;
130 rem %= SECS_PER_HOUR;
131 tp->tm_min = rem / 60;
132 tp->tm_sec = rem % 60;
133 /* January 1, 1970 was a Thursday. */
134 tp->tm_wday = (4 + days) % 7;
135 if (tp->tm_wday < 0)
136 tp->tm_wday += 7;
137 y = 1970;
139 #define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
140 #define LEAPS_THRU_END_OF(y) (DIV(y, 4) - DIV(y, 100) + DIV(y, 400))
142 while (days < 0 || days >= (leapyear(y) ? 366 : 365)) {
143 /* Guess a corrected year, assuming 365 days per year. */
144 long int yg = y + days / 365 - (days % 365 < 0);
146 /* Adjust DAYS and Y to match the guessed year. */
147 days -= ((yg - y) * 365 + LEAPS_THRU_END_OF(yg - 1) - LEAPS_THRU_END_OF(y - 1));
148 y = yg;
150 tp->tm_year = y - TM_YEAR_BASE;
151 if (tp->tm_year != y - TM_YEAR_BASE) {
152 /* The year cannot be represented due to overflow. */
153 // __set_errno (EOVERFLOW);
154 return 0;
156 tp->tm_yday = days;
157 ip = __mon_yday[leapyear(y)];
158 for (y = 11; days < (long int)ip[y]; --y)
159 continue;
160 days -= ip[y];
161 tp->tm_mon = y;
162 tp->tm_mday = days + 1;
163 return 1;
166 /* time_r function implementations */
167 // G: No time zones in our implementation so just do the converion from gtime_t to struct tm
168 struct gtm * __localtime_r(const gtime_t * t, struct gtm * tp)
170 __offtime(t, 0, tp);
171 return tp;
175 /* Return an integer value measuring (YEAR1-YDAY1 HOUR1:MIN1:SEC1) -
176 (YEAR0-YDAY0 HOUR0:MIN0:SEC0) in seconds, assuming that the clocks
177 were not adjusted between the time stamps.
179 The YEAR values uses the same numbering as TP->tm_year. Values
180 need not be in the usual range. However, YEAR1 must not be less
181 than 2 * INT_MIN or greater than 2 * INT_MAX.
183 The result may overflow. It is the caller's responsibility to
184 detect overflow. */
186 static inline gtime_t ydhms_diff(long int year1, long int yday1, int hour1, int min1, int sec1,
187 int year0, int yday0, int hour0, int min0, int sec0)
189 static_assert(-1 / 2 == 0, "no C99 integer division");
190 static_assert(INT_MAX <= LONG_MAX / 2 || TIME_T_MAX <= INT_MAX, "long int year and yday are not wide enough");
192 /* Compute intervening leap days correctly even if year is negative.
193 Take care to avoid integer overflow here. */
194 int a4 = SHR(year1, 2) + SHR(TM_YEAR_BASE, 2) - !(year1 & 3);
195 int b4 = SHR(year0, 2) + SHR(TM_YEAR_BASE, 2) - !(year0 & 3);
196 int a100 = a4 / 25 - (a4 % 25 < 0);
197 int b100 = b4 / 25 - (b4 % 25 < 0);
198 int a400 = SHR(a100, 2);
199 int b400 = SHR(b100, 2);
200 int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
202 /* Compute the desired time in gtime_t precision. Overflow might
203 occur here. */
204 gtime_t tyear1 = year1;
205 gtime_t years = tyear1 - year0;
206 gtime_t days = 365 * years + yday1 - yday0 + intervening_leap_days;
207 gtime_t hours = 24 * days + hour1 - hour0;
208 gtime_t minutes = 60 * hours + min1 - min0;
209 gtime_t seconds = 60 * minutes + sec1 - sec0;
210 return seconds;
213 /* Return a gtime_t value corresponding to (YEAR-YDAY HOUR:MIN:SEC),
214 assuming that *T corresponds to *TP and that no clock adjustments
215 occurred between *TP and the desired time.
216 If TP is null, return a value not equal to *T; this avoids false matches.
217 If overflow occurs, yield the minimal or maximal value, except do not
218 yield a value equal to *T. */
219 static gtime_t guess_time_tm(long int year, long int yday, int hour, int min, int sec,
220 gtime_t * t, struct gtm * tp)
222 if (tp) {
223 gtime_t d = ydhms_diff(year, yday, hour, min, sec,
224 tp->tm_year, tp->tm_yday,
225 tp->tm_hour, tp->tm_min, tp->tm_sec);
226 gtime_t t1 = *t + d;
227 if ((t1 < *t) == (TYPE_SIGNED(gtime_t) ? d < 0 : TIME_T_MAX / 2 < d))
228 return t1;
231 /* Overflow occurred one way or another. Return the nearest result
232 that is actually in range, except don't report a zero difference
233 if the actual difference is nonzero, as that would cause a false
234 match; and don't oscillate between two values, as that would
235 confuse the spring-forward gap detector. */
236 return (*t < TIME_T_MIDPOINT
237 ? (*t <= TIME_T_MIN + 1 ? *t + 1 : TIME_T_MIN)
238 : (TIME_T_MAX - 1 <= *t ? *t - 1 : TIME_T_MAX));
241 /* Use CONVERT to convert *T to a broken down time in *TP.
242 If *T is out of range for conversion, adjust it so that
243 it is the nearest in-range value and then convert that. */
244 static struct gtm * ranged_convert(struct gtm *(*convert)(const gtime_t *, struct gtm *), gtime_t * t, struct gtm * tp)
246 struct gtm * r = convert(t, tp);
248 if (!r && *t) {
249 gtime_t bad = *t;
250 gtime_t ok = 0;
252 /* BAD is a known unconvertible gtime_t, and OK is a known good one.
253 Use binary search to narrow the range between BAD and OK until
254 they differ by 1. */
255 while (bad != ok + (bad < 0 ? -1 : 1)) {
256 gtime_t mid = *t = (bad < 0 ? bad + ((ok - bad) >> 1) : ok + ((bad - ok) >> 1));
257 r = convert(t, tp);
258 if (r) ok = mid;
259 else bad = mid;
262 if (!r && ok) {
263 /* The last conversion attempt failed;
264 revert to the most recent successful attempt. */
265 *t = ok;
266 r = convert(t, tp);
270 return r;
273 /* Convert *TP to a gtime_t value, inverting
274 the monotonic and mostly-unit-linear conversion function CONVERT.
275 Use *OFFSET to keep track of a guess at the offset of the result,
276 compared to what the result would be for UTC without leap seconds.
277 If *OFFSET's guess is correct, only one CONVERT call is needed.
278 This function is external because it is used also by timegm.c. */
279 gtime_t __mktime_internal(struct gtm * tp,
280 struct gtm *(*convert)(const gtime_t *, struct gtm *),
281 gtime_t * offset)
283 gtime_t t, gt, t0, t1, t2;
284 struct gtm tm;
286 /* The maximum number of probes (calls to CONVERT) should be enough
287 to handle any combinations of time zone rule changes, solar time,
288 leap seconds, and oscillations around a spring-forward gap.
289 POSIX.1 prohibits leap seconds, but some hosts have them anyway. */
290 int remaining_probes = 6;
292 /* Time requested. Copy it in case CONVERT modifies *TP; this can
293 occur if TP is localtime's returned value and CONVERT is localtime. */
294 int sec = tp->tm_sec;
295 int min = tp->tm_min;
296 int hour = tp->tm_hour;
297 int mday = tp->tm_mday;
298 int mon = tp->tm_mon;
299 int year_requested = tp->tm_year;
301 /* Ensure that mon is in range, and set year accordingly. */
302 int mon_remainder = mon % 12;
303 int negative_mon_remainder = mon_remainder < 0;
304 int mon_years = mon / 12 - negative_mon_remainder;
305 long int lyear_requested = year_requested;
306 long int year = lyear_requested + mon_years;
308 /* The other values need not be in range:
309 the remaining code handles minor overflows correctly,
310 assuming int and gtime_t arithmetic wraps around.
311 Major overflows are caught at the end. */
313 /* Calculate day of year from year, month, and day of month.
314 The result need not be in range. */
315 int mon_yday = ((__mon_yday[leapyear(year)][mon_remainder + 12 * negative_mon_remainder]) - 1);
316 long int lmday = mday;
317 long int yday = mon_yday + lmday;
319 gtime_t guessed_offset = *offset;
321 int sec_requested = sec;
324 if (LEAP_SECONDS_POSSIBLE)
326 // Handle out-of-range seconds specially,
327 // since ydhms_tm_diff assumes every minute has 60 seconds.
328 if (sec < 0)
329 sec = 0;
330 if (59 < sec)
331 sec = 59;
335 /* Invert CONVERT by probing. First assume the same offset as last
336 time. */
338 t0 = ydhms_diff(year, yday, hour, min, sec, EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, -guessed_offset);
340 if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3) {
341 /* gtime_t isn't large enough to rule out overflows, so check
342 for major overflows. A gross check suffices, since if t0
343 has overflowed, it is off by a multiple of TIME_T_MAX -
344 TIME_T_MIN + 1. So ignore any component of the difference
345 that is bounded by a small value. */
347 /* Approximate log base 2 of the number of time units per
348 biennium. A biennium is 2 years; use this unit instead of
349 years to avoid integer overflow. For example, 2 average
350 Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,
351 which is 63113904 seconds, and rint (log2 (63113904)) is
352 26. */
353 int ALOG2_SECONDS_PER_BIENNIUM = 26;
354 int ALOG2_MINUTES_PER_BIENNIUM = 20;
355 int ALOG2_HOURS_PER_BIENNIUM = 14;
356 int ALOG2_DAYS_PER_BIENNIUM = 10;
357 int LOG2_YEARS_PER_BIENNIUM = 1;
359 int approx_requested_biennia =
360 (SHR(year_requested, LOG2_YEARS_PER_BIENNIUM)
361 - SHR(EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)
362 + SHR(mday, ALOG2_DAYS_PER_BIENNIUM)
363 + SHR(hour, ALOG2_HOURS_PER_BIENNIUM)
364 + SHR(min, ALOG2_MINUTES_PER_BIENNIUM)
365 + (LEAP_SECONDS_POSSIBLE
367 : SHR(sec, ALOG2_SECONDS_PER_BIENNIUM)));
369 int approx_biennia = SHR(t0, ALOG2_SECONDS_PER_BIENNIUM);
370 int diff = approx_biennia - approx_requested_biennia;
371 int abs_diff = diff < 0 ? -diff : diff;
373 /* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously
374 gives a positive value of 715827882. Setting a variable
375 first then doing math on it seems to work.
376 (ghazi@caip.rutgers.edu) */
377 gtime_t time_t_max = TIME_T_MAX;
378 gtime_t time_t_min = TIME_T_MIN;
379 gtime_t overflow_threshold = (time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;
381 if (overflow_threshold < abs_diff) {
382 /* Overflow occurred. Try repairing it; this might work if
383 the time zone offset is enough to undo the overflow. */
384 gtime_t repaired_t0 = -1 - t0;
385 approx_biennia = SHR(repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);
386 diff = approx_biennia - approx_requested_biennia;
387 abs_diff = diff < 0 ? -diff : diff;
388 if (overflow_threshold < abs_diff)
389 return -1;
390 guessed_offset += repaired_t0 - t0;
391 t0 = repaired_t0;
395 /* Repeatedly use the error to improve the guess. */
396 for (t = t1 = t2 = t0;
397 (gt = guess_time_tm(year, yday, hour, min, sec, &t, ranged_convert(convert, &t, &tm)), t != gt);
398 t1 = t2, t2 = t, t = gt) {
399 if (t == t1 && t != t2)
400 goto offset_found;
401 else if (--remaining_probes == 0)
402 return -1;
405 offset_found:
406 *offset = guessed_offset + t - t0;
408 if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec) {
409 /* Adjust time to reflect the tm_sec requested, not the normalized value.
410 Also, repair any damage from a false match due to a leap second. */
411 int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;
412 t1 = t + sec_requested;
413 t2 = t1 + sec_adjustment;
414 if (((t1 < t) != (sec_requested < 0))
415 | ((t2 < t1) != (sec_adjustment < 0))
416 | !convert(&t2, &tm))
417 return -1;
418 t = t2;
421 *tp = tm;
422 return t;
425 /* Convert *TP to a gtime_t value. */
426 gtime_t gmktime(struct gtm * tp)
428 // no time zone stuff. Just do the math ;)
429 static gtime_t localtime_offset;
430 return __mktime_internal(tp, __localtime_r, &localtime_offset);
433 /* Fill a (struct tm) TP* from a given gtime_t time stamp */
434 gtime_t filltm(const gtime_t * t, struct gtm * tp)
436 return __offtime(t, 0, tp);
439 gtime_t g_rtcTime;
440 uint8_t g_ms100 = 0; // global to allow time set function to reset to zero
442 void gettime(struct gtm * tm)
444 filltm(&g_rtcTime, tm); // create a struct tm date/time structure from global unix time stamp
447 void rtcGetTime(struct gtm * t);
449 #define RTC_ADJUST_PERIOD 60 // how often RTC is checked for accuracy [seconds]
450 #define RTC_ADJUST_TRESHOLD 20 // how much clock must differ before adjustment is made [seconds]
452 Changes RTC date/time to the given UTC date/time if:
453 * RTC_ADJUST_PERIOD seconds have elapsed from the last time this function adjusted the RTC clock
454 * AND if actual RTC clock differs from the given clock by more than RTC_ADJUST_TRESHOLD seconds
455 * Function does nothing for a minute around midnight, where date change could produce erroneous result
457 uint8_t rtcAdjust(uint16_t year, uint8_t mon, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec)
459 static tmr10ms_t lastRtcAdjust = 0;
460 if ((get_tmr10ms() - lastRtcAdjust) > (RTC_ADJUST_PERIOD * 100)) {
461 lastRtcAdjust = get_tmr10ms();
463 if (year == 0 || (hour == 0 && min == 0) || (hour == 23 && min == 59)) return 0;
465 // convert given UTC time to local time (to seconds) and compare it with RTC
466 struct gtm t;
467 t.tm_year = year - TM_YEAR_BASE;
468 t.tm_mon = mon - 1;
469 t.tm_mday = day;
470 t.tm_hour = hour;
471 t.tm_min = min;
472 t.tm_sec = sec;
473 gtime_t newTime = gmktime(&t) + g_eeGeneral.timezone * 3600;
474 gtime_t diff = (g_rtcTime > newTime) ? (g_rtcTime - newTime) : (newTime - g_rtcTime);
476 #if defined(DEBUG) && defined (PCBTARANIS)
477 struct gtm utm;
478 rtcGetTime(&utm);
479 gtime_t rtcTime = gmktime(&utm);
480 TRACE("rtc: %d, grtc: %d, gps: %d, diff: %d, ", rtcTime, g_rtcTime, newTime, diff);
481 #endif
483 if (diff > RTC_ADJUST_TRESHOLD) {
484 // convert newTime to struct gtm and set RTC clock
485 filltm(&newTime, &t);
486 g_rtcTime = gmktime(&t); // update local timestamp and get wday calculated
487 rtcSetTime(&t);
488 TRACE("RTC clock adjusted to %04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
489 // TODO perhaps some kind of audio notification ???
490 return 1;
493 return 0;