Revised LPF1+LPF2 filter cutoff bandwidths from STMicro (#13239)
[betaflight.git] / src / main / common / time.c
blob1bde8c1e9d334d9077d9847154a76b64a9cf801d
1 /*
2 * This file is part of Cleanflight, Betaflight and INAV.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
6 * You can obtain one at http://mozilla.org/MPL/2.0/.
8 * Alternatively, the contents of this file may be used under the terms
9 * of the GNU General Public License Version 3, as described below:
11 * This file is free software: you may copy, redistribute and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation, either version 3 of the License, or (at your
14 * option) any later version.
16 * This file is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
19 * Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see http://www.gnu.org/licenses/.
24 * @author Alberto Garcia Hierro <alberto@garciahierro.com>
27 #include <stdint.h>
28 #include <stdlib.h>
30 #include "platform.h"
32 #include "common/maths.h"
33 #include "common/printf.h"
34 #include "common/time.h"
36 #include "drivers/persistent.h"
38 #include "pg/pg_ids.h"
40 #include "drivers/time.h"
42 #ifdef USE_RTC_TIME
44 // For the "modulo 4" arithmetic to work, we need a leap base year
45 #define REFERENCE_YEAR 2000
46 // Offset (seconds) from the UNIX epoch (1970-01-01) to 2000-01-01
47 #define EPOCH_2000_OFFSET 946684800
49 #define MILLIS_PER_SECOND 1000
51 // rtcTime_t when the system was started.
52 // Calculated in rtcSet().
53 static rtcTime_t started = 0;
55 static const uint16_t days[4][12] =
57 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335},
58 { 366, 397, 425, 456, 486, 517, 547, 578, 609, 639, 670, 700},
59 { 731, 762, 790, 821, 851, 882, 912, 943, 974, 1004, 1035, 1065},
60 {1096, 1127, 1155, 1186, 1216, 1247, 1277, 1308, 1339, 1369, 1400, 1430},
63 PG_REGISTER_WITH_RESET_TEMPLATE(timeConfig_t, timeConfig, PG_TIME_CONFIG, 0);
65 PG_RESET_TEMPLATE(timeConfig_t, timeConfig,
66 .tz_offsetMinutes = 0,
69 static rtcTime_t dateTimeToRtcTime(dateTime_t *dt)
71 unsigned int second = dt->seconds; // 0-59
72 unsigned int minute = dt->minutes; // 0-59
73 unsigned int hour = dt->hours; // 0-23
74 unsigned int day = dt->day - 1; // 0-30
75 unsigned int month = dt->month - 1; // 0-11
76 unsigned int year = dt->year - REFERENCE_YEAR; // 0-99
77 int32_t unixTime = (((year / 4 * (365 * 4 + 1) + days[year % 4][month] + day) * 24 + hour) * 60 + minute) * 60 + second + EPOCH_2000_OFFSET;
78 return rtcTimeMake(unixTime, dt->millis);
81 static void rtcTimeToDateTime(dateTime_t *dt, rtcTime_t t)
83 int32_t unixTime = t / MILLIS_PER_SECOND - EPOCH_2000_OFFSET;
84 dt->seconds = unixTime % 60;
85 unixTime /= 60;
86 dt->minutes = unixTime % 60;
87 unixTime /= 60;
88 dt->hours = unixTime % 24;
89 unixTime /= 24;
91 unsigned int years = unixTime / (365 * 4 + 1) * 4;
92 unixTime %= 365 * 4 + 1;
94 unsigned int year;
95 for (year = 3; year > 0; year--) {
96 if (unixTime >= days[year][0]) {
97 break;
101 unsigned int month;
102 for (month = 11; month > 0; month--) {
103 if (unixTime >= days[year][month]) {
104 break;
108 dt->year = years + year + REFERENCE_YEAR;
109 dt->month = month + 1;
110 dt->day = unixTime - days[year][month] + 1;
111 dt->millis = t % MILLIS_PER_SECOND;
114 static void rtcGetDefaultDateTime(dateTime_t *dateTime)
116 dateTime->year = 0;
117 dateTime->month = 1;
118 dateTime->day = 1;
119 dateTime->hours = 0;
120 dateTime->minutes = 0;
121 dateTime->seconds = 0;
122 dateTime->millis = 0;
125 static bool rtcIsDateTimeValid(dateTime_t *dateTime)
127 return (dateTime->year >= REFERENCE_YEAR) &&
128 (dateTime->month >= 1 && dateTime->month <= 12) &&
129 (dateTime->day >= 1 && dateTime->day <= 31) &&
130 (dateTime->hours <= 23) &&
131 (dateTime->minutes <= 59) &&
132 (dateTime->seconds <= 59) &&
133 (dateTime->millis <= 999);
136 static void dateTimeWithOffset(dateTime_t *dateTimeOffset, dateTime_t *dateTimeInitial, int16_t minutes)
138 rtcTime_t initialTime = dateTimeToRtcTime(dateTimeInitial);
139 rtcTime_t offsetTime = rtcTimeMake(rtcTimeGetSeconds(&initialTime) + minutes * 60, rtcTimeGetMillis(&initialTime));
140 rtcTimeToDateTime(dateTimeOffset, offsetTime);
143 static bool dateTimeFormat(char *buf, dateTime_t *dateTime, int16_t offsetMinutes, bool shortVersion)
145 dateTime_t local;
147 int tz_hours = 0;
148 int tz_minutes = 0;
149 bool retVal = true;
151 // Apply offset if necessary
152 if (offsetMinutes != 0) {
153 tz_hours = offsetMinutes / 60;
154 tz_minutes = abs(offsetMinutes % 60);
155 dateTimeWithOffset(&local, dateTime, offsetMinutes);
156 dateTime = &local;
159 if (!rtcIsDateTimeValid(dateTime)) {
160 rtcGetDefaultDateTime(&local);
161 dateTime = &local;
162 retVal = false;
165 if (shortVersion) {
166 tfp_sprintf(buf, "%04u-%02u-%02u %02u:%02u:%02u",
167 dateTime->year, dateTime->month, dateTime->day,
168 dateTime->hours, dateTime->minutes, dateTime->seconds);
169 } else {
170 // Changes to this format might require updates in
171 // dateTimeSplitFormatted()
172 // Datetime is in ISO_8601 format, https://en.wikipedia.org/wiki/ISO_8601
173 tfp_sprintf(buf, "%04u-%02u-%02uT%02u:%02u:%02u.%03u%c%02d:%02d",
174 dateTime->year, dateTime->month, dateTime->day,
175 dateTime->hours, dateTime->minutes, dateTime->seconds, dateTime->millis,
176 tz_hours >= 0 ? '+' : '-', abs(tz_hours), tz_minutes);
179 return retVal;
182 rtcTime_t rtcTimeMake(int32_t secs, uint16_t millis)
184 return ((rtcTime_t)secs) * MILLIS_PER_SECOND + millis;
187 int32_t rtcTimeGetSeconds(rtcTime_t *t)
189 return *t / MILLIS_PER_SECOND;
192 uint16_t rtcTimeGetMillis(rtcTime_t *t)
194 return *t % MILLIS_PER_SECOND;
197 bool dateTimeFormatUTC(char *buf, dateTime_t *dt)
199 return dateTimeFormat(buf, dt, 0, false);
202 bool dateTimeFormatLocal(char *buf, dateTime_t *dt)
204 const int16_t timezoneOffset = rtcIsDateTimeValid(dt) ? timeConfig()->tz_offsetMinutes : 0;
205 return dateTimeFormat(buf, dt, timezoneOffset, false);
208 bool dateTimeFormatLocalShort(char *buf, dateTime_t *dt)
210 return dateTimeFormat(buf, dt, timeConfig()->tz_offsetMinutes, true);
213 void dateTimeUTCToLocal(dateTime_t *utcDateTime, dateTime_t *localDateTime)
215 dateTimeWithOffset(localDateTime, utcDateTime, timeConfig()->tz_offsetMinutes);
218 bool dateTimeSplitFormatted(char *formatted, char **date, char **time)
220 // Just look for the T and replace it with a zero
221 // XXX: Keep in sync with dateTimeFormat()
222 for (char *p = formatted; *p; p++) {
223 if (*p == 'T') {
224 *date = formatted;
225 *time = (p+1);
226 *p = '\0';
227 return true;
230 return false;
233 bool rtcHasTime(void)
235 return started != 0;
238 bool rtcGet(rtcTime_t *t)
240 if (!rtcHasTime()) {
241 return false;
243 *t = started + millis();
244 return true;
247 bool rtcSet(rtcTime_t *t)
249 started = *t - millis();
250 return true;
253 bool rtcGetDateTime(dateTime_t *dt)
255 rtcTime_t t;
256 if (rtcGet(&t)) {
257 rtcTimeToDateTime(dt, t);
258 return true;
260 // No time stored, fill dt with 0000-01-01T00:00:00.000
261 rtcGetDefaultDateTime(dt);
262 return false;
265 bool rtcSetDateTime(dateTime_t *dt)
267 rtcTime_t t = dateTimeToRtcTime(dt);
268 return rtcSet(&t);
271 #if defined(USE_PERSISTENT_OBJECTS)
272 void rtcPersistWrite(int16_t offsetMinutes)
274 rtcTime_t workTime;
275 uint32_t highLongWord = 0;
276 uint32_t lowLongWord = 0;
277 if (rtcGet(&workTime)) {
278 workTime += (offsetMinutes * 60 * MILLIS_PER_SECOND);
279 highLongWord = (uint32_t)(workTime >> 32);
280 lowLongWord = (uint32_t)(workTime & 0xffffffff);
282 persistentObjectWrite(PERSISTENT_OBJECT_RTC_HIGH, highLongWord);
283 persistentObjectWrite(PERSISTENT_OBJECT_RTC_LOW, lowLongWord);
286 bool rtcPersistRead(rtcTime_t *t)
288 const uint32_t highLongWord = persistentObjectRead(PERSISTENT_OBJECT_RTC_HIGH);
289 const uint32_t lowLongWord = persistentObjectRead(PERSISTENT_OBJECT_RTC_LOW);
291 if ((highLongWord != 0) || (lowLongWord != 0)) {
292 *t = ((uint64_t)highLongWord << 32) + lowLongWord;
293 return true;
294 } else {
295 return false;
298 #endif
299 #endif // USE_RTC_TIME