Newer
Older
Tardis / src / Clock.cpp
/*
SPDX-License-Identifier: MIT
Copyright (c) 2023 John Watts and the LuminaSensum contributors
*/

#include "Clock.h"
#include "mbed.h"
#include <atomic>

using namespace std::chrono;

typedef time_point<Kernel::Clock> kernel_timepoint;

Mutex clock_lock;
microseconds clock_offset;

struct timeval get_rtc_time() {
	struct timeval timeval;
	timeval.tv_sec = time(NULL);
	timeval.tv_usec = 0;
	return timeval;
}

void clock_setup(void) {
	microseconds new_offset(0);
	struct timeval rtc_time = get_rtc_time();
	kernel_timepoint kernel_time = Kernel::Clock::now();
	microseconds boot_offset = kernel_time.time_since_epoch();
	new_offset += microseconds(rtc_time.tv_usec);
	new_offset += seconds(rtc_time.tv_sec);
	new_offset -= boot_offset;
	clock_lock.lock();
	clock_offset = new_offset;
	clock_lock.unlock();
}

struct timeval clock_time() {
	clock_lock.lock();
	kernel_timepoint kernel_time = Kernel::Clock::now();
	microseconds curr_offset = clock_offset;
	clock_lock.unlock();
	microseconds boot_offset = kernel_time.time_since_epoch();
	microseconds offsetted = curr_offset + boot_offset;
	uint64_t microsecs = offsetted.count();
	struct timeval time;
	time.tv_sec = microsecs / 1000000;
	time.tv_usec = microsecs % 1000000;
	return time;
}

SNTPError clock_sync(NetworkInterface &net) {
	struct timeval time;
	SNTPError err;
	err = sntp(net, "time.google.com", 123, &time);
	if (err != SNTPSuccess) {
		return err;
	}
	clock_lock.lock();
	microseconds new_offset(0);
	kernel_timepoint kernel_time = Kernel::Clock::now();
	microseconds boot_offset = kernel_time.time_since_epoch();
	new_offset += microseconds(time.tv_usec);
	new_offset += seconds(time.tv_sec);
	new_offset -= boot_offset;
	clock_offset = new_offset;
	clock_lock.unlock();
	set_time(time.tv_sec);
	return SNTPSuccess;
}

const char *clock_timestring(struct timeval time, const char *format) {
	struct tm *local_time = localtime(&time.tv_sec);

	static char buffer[64];
	size_t buffer_size = sizeof(buffer);
	size_t buffer_written;
	buffer_written = strftime(buffer, buffer_size, format, local_time);

	if (buffer_written == 0) {
		// It overflowed, so return something useful
		return "(time overflow)";
	} else {
		return buffer;
	}
}

const char *clock_iso8601string(void) {
	struct timeval time = clock_time();
	const char *timestring = clock_timestring(time, "%Y-%m-%dT%H:%M:%S");

	static char buffer[128];
	int buffer_size = sizeof(buffer);
	int written;
	written = snprintf(
		buffer, buffer_size, "%s.%05li", timestring, time.tv_usec);

	if (written >= buffer_size) {
		// It overflowed, so return something useful
		return "(iso8601 overflow)";
	} else {
		return buffer;
	}
}