From 2cb9b6fdc38a5df00ade737f90a9daf685ab89c3 Mon Sep 17 00:00:00 2001 From: flu0r1ne Date: Thu, 13 Jul 2023 12:20:29 -0500 Subject: Init commit --- src/models/counter.cpp | 35 +++++++++++ src/models/counter.hpp | 33 +++++++++++ src/models/timer.cpp | 153 +++++++++++++++++++++++++++++++++++++++++++++++++ src/models/timer.hpp | 126 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+) create mode 100644 src/models/counter.cpp create mode 100644 src/models/counter.hpp create mode 100644 src/models/timer.cpp create mode 100644 src/models/timer.hpp (limited to 'src/models') diff --git a/src/models/counter.cpp b/src/models/counter.cpp new file mode 100644 index 0000000..4855c24 --- /dev/null +++ b/src/models/counter.cpp @@ -0,0 +1,35 @@ +#include "models/counter.hpp" + +double FreqCounter::rate() const { + uint64_t ms = _timer->total_duration_ms(); + + if(_timer->overtime()) { + ms = _timer->preset_ms(); + } + + return static_cast(_cnt) / (ms / 60.0e3); +} + + +double FreqCounter::overtime_rate() const { + return static_cast(_overtime_cnt) / (_timer->total_duration_ms() / 60.0e3); +} + +std::string FreqCounter::display_value() const { + if(_cnt == UINT32_MAX) { + return "ERR"; + } + + return std::to_string(_cnt); +} + +void FreqCounter::inc() { + + if(_overtime_cnt != UINT32_MAX) + _overtime_cnt++; + + if(!_timer->overtime()) { + _cnt = _overtime_cnt; + } + +} diff --git a/src/models/counter.hpp b/src/models/counter.hpp new file mode 100644 index 0000000..d1b68fc --- /dev/null +++ b/src/models/counter.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include "models/timer.hpp" + +class FreqCounter { + + public: + + FreqCounter(FreqTimer const & timer) + : _timer(&timer) + {} + + void reset() { _cnt = _overtime_cnt = 0; } + + void inc(); + + uint32_t cnt() const { return _cnt; } + + [[nodiscard]] double rate() const; + + [[nodiscard]] double overtime_rate() const; + + [[nodiscard]] std::string display_value() const; + + + private: + + uint32_t _cnt; + uint32_t _overtime_cnt; + FreqTimer const * _timer; +}; diff --git a/src/models/timer.cpp b/src/models/timer.cpp new file mode 100644 index 0000000..a05e8c1 --- /dev/null +++ b/src/models/timer.cpp @@ -0,0 +1,153 @@ +#include "timer.hpp" +#include + +#ifdef EMSCRIPTEN +#include +#endif + +#ifdef TEST +#define OVERRIDABLE_DURING_TESTING __attribute__((weak)) +#else +#define OVERRIDABLE_DURING_TESTING static inline +#endif + + +OVERRIDABLE_DURING_TESTING uint64_t monotonic_timestamp_ms() { +#ifdef EMSCRIPTEN + return emscripten_get_now(); +#else + return 0.0; // TODO: unimplemented +#endif +} + +namespace time_const { + static const int64_t MSEC = 1; + static const int64_t SEC = 1000 * MSEC; + static const int64_t MIN = 60 * SEC; + static const int64_t HOUR = 60 * MIN; +}; + +FreqTimer::FreqTimer() { + clear(); +} + +static void _append_ratio(std::string & display_val, int64_t & ms, int64_t ratio, bool two_digit = true) { + int64_t amount = ms / ratio; + ms -= amount * ratio; + + char buf[3]; + + if(two_digit) { + snprintf(buf, sizeof(buf), "%02" PRIi64, amount); + } else { + snprintf(buf, sizeof(buf), "%1" PRIi64, amount); + } + + display_val.append(buf); +} + +std::string FreqTimer::counter_display_value() const { + using namespace time_const; + + int64_t ms = time_ms(); + + std::string display_val; + + if(ms < 0) { + display_val.append("+ "); + ms = -ms; + } + + if(ms > 99 * HOUR) { + return "MAX TIME EXCEEDED"; + } + + _append_ratio(display_val, ms, HOUR); + display_val.append(" : "); + _append_ratio(display_val, ms, MIN); + display_val.append(" : "); + _append_ratio(display_val, ms, SEC); + display_val.append("."); + _append_ratio(display_val, ms, 100 * MSEC, false); + + return display_val; +} + +void FreqTimer::increment_preset_ms(uint64_t amount_ms) { + if(_state != State::SETTING) { + clear(); + } + + _preset_ms += amount_ms; +} + +void FreqTimer::toggleStart() { + if(_state == State::RUNNING) { + _stop(); + } else { + _start(); + } +} + +bool FreqTimer::has_preset() const { + return _preset_ms != 0; +} + +void FreqTimer::reset() { + _time_offset_ms = 0; + _time_start_ms = monotonic_timestamp_ms(); + _state = State::RESET; +} + +void FreqTimer::clear() { + _time_offset_ms = 0; + _time_start_ms = monotonic_timestamp_ms(); + _preset_ms = 0; + _state = State::SETTING; +} + +double FreqTimer::record_floor() const { + + int64_t ms; + if(has_preset()) { + ms = _preset_ms; + } else { + ms = total_duration_ms(); + } + + return 60.0e3 / ms; +} + +double FreqTimer::overtime_record_floor() const { + return 60.0e3 / total_duration_ms(); +} + +uint64_t FreqTimer::_duration_since_last_stop_ms() const { + if(_state != State::RUNNING) { + return 0; + } + + return monotonic_timestamp_ms() - _time_start_ms; +} + +int64_t FreqTimer::time_ms() const { + if (_preset_ms == 0) { + return total_duration_ms(); + } + + return static_cast(_preset_ms) - total_duration_ms(); +} + +uint64_t FreqTimer::total_duration_ms() const { + return _time_offset_ms + _duration_since_last_stop_ms(); +} + +void FreqTimer::_stop() { + _time_offset_ms += _duration_since_last_stop_ms(); + _state = State::STOPPED; +} + +void FreqTimer::_start() { + _time_start_ms = monotonic_timestamp_ms(); + _state = State::RUNNING; +} diff --git a/src/models/timer.hpp b/src/models/timer.hpp new file mode 100644 index 0000000..0b0a978 --- /dev/null +++ b/src/models/timer.hpp @@ -0,0 +1,126 @@ +#pragma once + +#include +#include + +class FreqTimer { + public: + + FreqTimer(); + + /* + * Starts the timer if stopped + */ + void toggleStart(); + + /* + * Timer is running + */ + [[nodiscard]] bool running() const { return _state == State::RUNNING; } + + /* + * Timer has been started + */ + [[nodiscard]] bool started() const { + return _state == State::RUNNING or _state == State::STOPPED; + } + + /* + * Current preset in ms + */ + [[nodiscard]] int64_t preset_ms() const noexcept { return _preset_ms; } + + /* + * Whether the preset time has been exceeded + */ + [[nodiscard]] bool overtime() const { return time_ms() < 0; } + + /* + * Reset the time + */ + void reset(); + + /* + * Reset the time and preset + */ + void clear(); + + [[nodiscard]] std::string counter_display_value() const; + + /* + * Lowest frequency which can be measured + * in the interval (as measured in events/min + * or multiples of 60 Hz) + */ + [[nodiscard]] double record_floor() const; + + + [[nodiscard]] double overtime_record_floor() const; + + /* + * Without a preset, the timer acts like a + * stopwatch and counts freely from zero. + * With a preset, the timer acts like a timer + * counting down to zero, then counts up to + * measure the number of seconds since the + * timing has ended. + */ + [[nodiscard]] bool has_preset() const; + + /* + * The current duration in milliseconds. + * + * If the timer has exceeded the preset, + * this time will be negative. + */ + [[nodiscard]] int64_t time_ms() const; + + [[nodiscard]] uint64_t total_duration_ms() const; + + + /* + * Increment the timer's preset by the specified + * amount (in milisec.) Subsequent calls between + * timings increment the time. Incrementing a + * timing after a timing has been restarted will + * reset the timing and start from zero. + */ + void increment_preset_ms(uint64_t amount_ms); + + private: + + enum class State { + // The timer initially starts in + // "setting" mode and the preset + // can be modified by incrementing + // it by values (measured in seconds) + // + // Once it is started, this preset + // is "sticky" and a reset returns + // the timer to the preset value. + // Incrementing the preset or clearing + // the timer moves the timer back + // into setting mode. + SETTING, + // Timer is active + RUNNING, + // Timer is temporarily stopped + STOPPED, + // Reset + RESET, + }; + + State _state; + + uint64_t _time_offset_ms; + uint64_t _time_start_ms; + + int64_t _preset_ms; + + [[nodiscard]] uint64_t _duration_since_last_stop_ms() const; + + + void _stop(); + void _start(); + +}; -- cgit v1.2.3