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"

using namespace std::chrono;

Mutex clock_lock;
microseconds boot_start;

struct timeval microseconds_to_timeval(microseconds val) {
	uint64_t microsecs = val.count();
	struct timeval time;
	time.tv_sec = microsecs / 1000000;
	time.tv_usec = microsecs % 1000000;
	return time;
}

microseconds timeval_to_microseconds(struct timeval val) {
	microseconds time(0);
	time += seconds(val.tv_sec);
	time += microseconds(val.tv_usec);
	return time;
}

void clock_setup(void) {
	kernel_timepoint kernel_time = Kernel::Clock::now();
	seconds real_time = seconds(time(NULL));
	microseconds uptime = kernel_time.time_since_epoch();
	clock_lock.lock();
	boot_start = real_time - uptime;
	clock_lock.unlock();
}

microseconds clock_time(kernel_timepoint kernel_time) {
	clock_lock.lock();
	microseconds curr_offset = boot_start;
	clock_lock.unlock();
	microseconds uptime = kernel_time.time_since_epoch();
	return boot_start + uptime;
}

kernel_timepoint kernel_time(microseconds clock_time) {
	clock_lock.lock();
	microseconds curr_offset = boot_start;
	clock_lock.unlock();
	microseconds boot_offset = clock_time - curr_offset;
	milliseconds offset_ms = duration_cast<milliseconds>(boot_offset);
	return kernel_timepoint(offset_ms);
}

std::chrono::microseconds clock_now(void) {
	return clock_time(Kernel::Clock::now());
}

SNTPError clock_sync(NetworkInterface &net) {
	struct timeval time;
	SNTPError err;
	err = sntp(net, "time.google.com", 123, &time);
	if (err != SNTPSuccess) {
		return err;
	}
	kernel_timepoint kernel_time = Kernel::Clock::now();
	microseconds real_time = timeval_to_microseconds(time);
	microseconds uptime = kernel_time.time_since_epoch();
	clock_lock.lock();
	boot_start = real_time - uptime;
	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) {
	microseconds time = clock_now();
	struct timeval val = microseconds_to_timeval(time);
	const char *timestring = clock_timestring(val, "%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, val.tv_usec);

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