SatNOGS-COMMS  4.1.0
A COMMS subsystem for CubeSats
Loading...
Searching...
No Matches
time.cpp
Go to the documentation of this file.
1/*
2 * SatNOGS-COMMS MCU software
3 *
4 * Copyright (C) 2024, Libre Space Foundation <http://libre.space>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * SPDX-License-Identifier: GNU General Public License v3.0 or later
20 */
21
22#include "time.hpp"
23#include "error_handler.hpp"
24#include "scoped_lock.hpp"
25#include <etl/string.h>
26#include <etl/string_stream.h>
28#include <stm32_ll_pwr.h>
29
30static const struct device *rtc = DEVICE_DT_GET(DT_ALIAS(rtc));
31
32static satnogs::comms::time *time_inst = nullptr;
33
34#if DT_NODE_EXISTS(DT_ALIAS(gnss))
35static const struct device *gnss_uart = DEVICE_DT_GET(DT_ALIAS(uartgnss));
36
37static void
38gnss_data_callback_wrapper(const struct device *dev,
39 const struct gnss_data *data)
40{
41 if (time_inst) {
42 time_inst->gnss_data_cb(dev, data);
43 }
44}
45
46GNSS_DATA_CALLBACK_DEFINE(DEVICE_DT_GET(DT_ALIAS(gnss)),
47 gnss_data_callback_wrapper);
48#endif
49
50namespace satnogs::comms
51{
52
53void
54time::gnss_data_cb(const struct device *dev, const struct gnss_data *data)
55{
56 struct tm time_info;
57 struct rtc_time datetime_set;
58 time_t posix_epoch;
59
60 if (data->info.fix_status != GNSS_FIX_STATUS_NO_FIX) {
61 m_gnss_received_fix = true;
62 time_info.tm_hour = data->utc.hour;
63 time_info.tm_min = data->utc.minute;
64 time_info.tm_sec = data->utc.millisecond / 1000;
65 time_info.tm_mday = data->utc.month_day;
66 time_info.tm_mon = data->utc.month - 1; // Months are 0-11 in struct tm
67 time_info.tm_year = data->utc.century_year + 100; // Years since 1900
68 time_info.tm_isdst = -1; // Disables daylight saving time (DST)
69
70 posix_epoch = timeutil_timegm(&time_info);
71
72 m_millis_since_epoch_gnss =
73 posix_epoch * 1000 + data->utc.millisecond % 1000;
74 m_last_gnss_update_uptime = k_uptime_get();
75
76 // Set RTC time
77 gmtime_r(&posix_epoch, rtc_time_to_tm(&datetime_set));
78 datetime_set.tm_nsec = (data->utc.millisecond % 1000) * 1000000;
79 rtc_set_time(m_rtc, &datetime_set);
80
81 /*
82 * BUG: https://github.com/zephyrproject-rtos/zephyr/issues/90942
83 * Until this is fixed, we manually re-enable the access to the backup
84 * domain
85 */
86 LL_PWR_EnableBkUpAccess();
87
88 scoped_lock lock(&m_mtx);
89 m_gnss_data = *data;
90 }
91 /*
92 * Update the timestamp of the last valid NMEA message. Even without a fix
93 * this can be used to identify communication with the GNSS
94 */
95 m_last_nmea_update_uptime = k_uptime_get();
96}
97
111time::get(uint64_t &t)
112{
113 struct rtc_time datetime_get;
114 int rtc_status = rtc_get_time(m_rtc, &datetime_get);
115
116 /*
117 * Regardless the GNSS status, we trust the RTC. Every GNSS event updates the
118 * RTC, so even with a GNSS downtime, RTC will hold the most accurate time.
119 * In addition even with no RTC battery or a failed battery, the RTC
120 * peripheral is operational while the board is powered. So if a telecommand
121 * issues an update to the RTC, the RTC subsystem will return a valid time
122 * information
123 */
124 if (rtc_status == 0) {
125 t = timeutil_timegm(rtc_time_to_tm(&datetime_get)) * 1000 +
126 datetime_get.tm_nsec / 1000000;
128 }
129
130 /* No RTC, but we keep track of time based on the uptime and the last known
131 * GNSS fix */
132 if (m_gnss_received_fix) {
133 t = m_millis_since_epoch_gnss +
134 (k_uptime_get() - last_gnss_update_uptime());
135 return time_src::GNSS_ONLY;
136 }
137
138 /* Last resort... */
139 t = uptime();
140 return time_src::UPTIME;
141}
142
152time::get(struct tm &t)
153{
154 uint64_t ms_since_epoch;
155 auto ret = time::get(ms_since_epoch);
156 time_t posix_epoch = ms_since_epoch / 1000;
157 gmtime_r(&posix_epoch, &t);
158 return ret;
159}
160
166uint64_t
168{
169 return k_uptime_get() & (~BIT64(63));
170}
171
178uint64_t
180{
181 return m_last_gnss_update_uptime;
182}
183
184uint64_t
186{
187 return m_last_nmea_update_uptime;
188}
189
199void
200time::set(const struct tm &t)
201{
202
203 /* RTC needs the week day. Instead of requesting it from the operator (sic...)
204 * we can calculate it
205 */
206 struct tm tmp = t;
207 auto tt = mktime(&tmp);
208 auto ret = gmtime_r(&tt, &tmp);
209 if (ret == nullptr) {
210 return;
211 }
212
213 struct rtc_time rtct = {.tm_sec = ret->tm_sec,
214 .tm_min = ret->tm_min,
215 .tm_hour = ret->tm_hour,
216 .tm_mday = ret->tm_mday,
217 .tm_mon = ret->tm_mon,
218 .tm_year = ret->tm_year,
219 .tm_wday = ret->tm_wday,
220 .tm_yday = -1,
221 .tm_nsec = 0};
222
223 rtc_set_time(m_rtc, &rtct);
224 /*
225 * BUG: https://github.com/zephyrproject-rtos/zephyr/issues/90942
226 * Until this is fixed, we manually re-enable the access to the backup domain
227 */
228 LL_PWR_EnableBkUpAccess();
229}
230
236void
237time::gnss(struct gnss_data &data)
238{
239 scoped_lock lock(&m_mtx);
240 data = m_gnss_data;
241}
242
244 : m_millis_since_epoch_gnss(0),
245 m_gnss_received_fix(false),
246 m_last_gnss_update_uptime(0),
247 m_last_nmea_update_uptime(0),
248 m_rtc(rtc)
249{
250
251 k_mutex_init(&m_mtx);
252
253#if DT_NODE_EXISTS(DT_ALIAS(gnss))
254 auto &err = error_handler::get_instance();
255 if (!device_is_ready(gnss_uart)) {
256 err.assert_error<device_not_ready_exception>(__FILE__, __LINE__);
257 }
258
259 struct uart_config gnss_uart_cfg = {
260 .baudrate = CONFIG_GNSS_UART_BAUDRATE,
261 .parity = UART_CFG_PARITY_NONE,
262 .stop_bits = UART_CFG_STOP_BITS_1,
263 .data_bits = UART_CFG_DATA_BITS_8,
264 .flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
265 };
266
267 int rc = uart_configure(gnss_uart, &gnss_uart_cfg);
268
269 if (rc) {
270 err.assert_error<device_not_configured_exception>(__FILE__, __LINE__);
271 }
272
273#endif
274
275 time_inst = this;
276}
277
289size_t
290time::to_string(const struct tm &tm, etl::istring &s,
291 time::time_granularity granularity) const
292{
293
294 size_t bytes = 0;
295
296 switch (granularity) {
298 bytes = strftime(s.data_end(), s.available(), "%Y", &tm);
299 break;
301 bytes = strftime(s.data_end(), s.available(), "%Y-%m", &tm);
303 bytes = strftime(s.data_end(), s.available(), "%F", &tm);
304 break;
306 bytes = strftime(s.data_end(), s.available(), "%FT%HZ", &tm);
307 break;
309 bytes = strftime(s.data_end(), s.available(), "%FT%H:%MZ", &tm);
310 break;
312 bytes = strftime(s.data_end(), s.available(), "%FT%TZ", &tm);
313 break;
314 default:
315 throw to_string_exception(__FILE__, __LINE__);
316 break;
317 }
318 s.trim_to_terminator();
319 return bytes;
320}
321
325size_t
326time::to_string(const struct tm &tm, etl::istring &s) const
327{
328 size_t bytes = 0;
329 bytes = strftime(s.data_end(), s.available(), "%F", &tm);
330 s.trim_to_terminator();
331 return bytes;
332}
333
334} // namespace satnogs::comms
static error_handler & get_instance()
Singleton access to the error_handler subsystem.
Implements a scoped lock utilizing the Zephyr mutex.
Time and position information.
Definition time.hpp:60
void set(const struct tm &t)
Sets the RTC time.
Definition time.cpp:200
void gnss(struct gnss_data &data)
Retrieve latest GNSS information.
Definition time.cpp:237
time_src get(uint64_t &t)
Gets the time in ms.
Definition time.cpp:111
uint64_t uptime()
Gets the current system uptime in milliseconds.
Definition time.cpp:167
uint64_t last_gnss_update_uptime()
Gets the system uptime (in milliseconds) at the time of the last GNSS fix.
Definition time.cpp:179
uint64_t last_nmea_update_uptime()
Definition time.cpp:185
size_t to_string(const struct tm &tm, etl::istring &s, time::time_granularity granularity) const
Return the string represantation of a struct tm time instance following the ISO-8601 paradigm....
Definition time.cpp:290
time_granularity
Granularity of time represented as string.
Definition time.hpp:90
void gnss_data_cb(const struct device *dev, const struct gnss_data *data)
Definition time.cpp:54
time(time const &)=delete
time_src
Source of the reported time.
Definition time.hpp:74
@ UPTIME
No RTC or GNSS time source. The uptime is used to track time.
Definition time.hpp:80
@ GNSS_ONLY
No RTC installed, but there is time information from a GNSS fix.
Definition time.hpp:78