NanoTime.h
Go to the documentation of this file.
1 /****
2  * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
3  * Created 2015 by Skurydin Alexey
4  * http://github.com/SmingHub/Sming
5  * All files of the Sming Core are provided under the LGPL v3 license.
6  *
7  * NanoTime.h - Utilities for handling time periods at nanosecond resolution
8  *
9  * @author mikee47 <mike@sillyhouse.net>
10  *
11  * Note: C++ provides the `chrono` utilities but is not well-suited for embedded applications:
12  *
13  * - Only uses 64-bit calculations. NanoTime can use any integral type, although uint32_t is the most useful.
14  * - Time conversions are truncated. A value of 0.99 seconds would be treated as 0.
15  * NanoTime rounds results so would return 1.
16  * - Supports compile-time calculations but no runtime calculation support.
17  *
18  ****/
19 
20 #pragma once
21 
22 #include <cstdint>
23 #include <esp_attr.h>
24 #include <sming_attr.h>
25 #include <cmath>
26 #include "Rational.h"
27 #include <WString.h>
28 
39 namespace NanoTime
40 {
45 enum Unit {
54 };
55 
59 const char* unitToString(Unit unit);
60 
64 const char* unitToLongString(Unit unit);
65 
69 struct Frequency {
71  {
72  }
73 
74  operator uint32_t()
75  {
76  return frequency;
77  }
78 
88  String toString() const;
89 
90  uint32_t frequency;
91 };
92 
96 constexpr BasicRatio32 unitTicks[UnitMax + 1] = {
97  {1000000000, 1}, // Nanoseconds
98  {1000000, 1}, // Microseconds
99  {1000, 1}, // Milliseconds
100  {1, 1}, // Seconds
101  {1, 60}, // Minutes
102  {1, 60 * 60}, // Hours
103  {1, 24 * 60 * 60}, // Days
104 };
105 
111 template <Unit unit> using UnitTickRatio = std::ratio<unitTicks[unit].num, unitTicks[unit].den>;
112 
113 // Forward declarations
114 template <class Clock_, Unit unit_, uint64_t time_> struct TimeConst;
115 template <class Clock_, uint64_t ticks_> struct TicksConst;
116 template <class Clock_, Unit unit_, typename TimeType_> struct TimeSource;
117 template <typename T> struct Time;
118 template <typename Clock_, typename T> struct Ticks;
119 
131 template <typename ClockDef, uint32_t frequency_, typename TickType_, TickType_ maxTicks_> struct Clock {
132  using TickType = TickType_;
133  template <uint64_t ticks> using TicksConst = TicksConst<Clock, ticks>;
134  template <Unit unit, uint64_t time> using TimeConst = TimeConst<Clock, unit, time>;
135  template <Unit unit> using TicksPerUnit = std::ratio_divide<std::ratio<frequency_>, UnitTickRatio<unit>>;
136  template <Unit unit, typename TimeType> using TimeSource = TimeSource<Clock, unit, TimeType>;
137  template <typename T> using Ticks = Ticks<Clock, T>;
138 
139  static constexpr const char* typeName()
140  {
141  return ClockDef::typeName();
142  }
143 
144  static constexpr uint32_t frequency()
145  {
146  return frequency_;
147  }
148 
150  static constexpr MaxTicks maxTicks()
151  {
152  return MaxTicks();
153  }
154 
155  template <Unit unit> using MaxTime = typename MaxTicks::template TimeConst<unit>;
156  template <Unit unit> static constexpr MaxTime<unit> maxTime()
157  {
158  return MaxTime<unit>();
159  }
160 
165  static Ratio32 ticksPerUnit(Unit unit)
166  {
167  const auto& tpu = unitTicks[unit];
168  return Ratio32(frequency_ * tpu.den, tpu.num);
169  }
170 
176  template <Unit unit, uint64_t time> static constexpr TimeConst<unit, time> timeConst()
177  {
178  return TimeConst<unit, time>();
179  }
180 
186  template <uint64_t ticks> static constexpr TicksConst<ticks> ticksConst()
187  {
188  return TicksConst<ticks>();
189  }
190 
196  template <Unit unit, typename TimeType> static constexpr TimeSource<unit, TimeType> timeSource()
197  {
199  }
200 
206  template <Unit unit, typename TimeType> static Ticks<TimeType> timeToTicks(TimeType time)
207  {
209  }
210 
216  template <Unit unit, typename TimeType> static Time<TimeType> ticksToTime(TimeType ticks)
217  {
219  }
220 
221  static String toString()
222  {
223  String s;
224  s += typeName();
225  s += '/';
226  s += Frequency(frequency()).toString();
227  return s;
228  }
229 };
230 
241 template <uint64_t time, Unit unitsFrom, Unit unitsTo,
242  typename R = std::ratio_divide<UnitTickRatio<unitsTo>, UnitTickRatio<unitsFrom>>>
243 constexpr uint64_t convert()
244 {
245  return ({ round(double(time) * R::num / R::den); });
246 }
247 
256 template <typename TimeType> __forceinline TimeType convert(const TimeType& time, Unit unitsFrom, Unit unitsTo)
257 {
258  if(unitsFrom == unitsTo) {
259  return time;
260  }
261 
262  using R = Ratio<TimeType>;
263  auto ratio = R(unitTicks[unitsTo]) / R(unitTicks[unitsFrom]);
264  return time * ratio;
265 }
266 
271 struct TimeValue {
272  TimeValue() = default;
273 
279  template <typename TimeType> TimeValue(Unit unit, TimeType time)
280  {
281  set(unit, time);
282  }
283 
284  template <typename TimeType> void set(Unit unit, TimeType time);
285 
289  uint32_t getMicroseconds() const
290  {
291  return microseconds + (milliseconds * 1000);
292  }
293 
297  uint32_t getNanoseconds() const
298  {
299  return nanoseconds + getMicroseconds() * 1000;
300  }
301 
302  String toString() const;
303 
304  operator String() const
305  {
306  return toString();
307  }
308 
309  bool overflow = false;
311  uint32_t days = 0;
312  uint8_t hours = 0;
313  uint8_t minutes = 0;
314  uint8_t seconds = 0;
315  uint16_t milliseconds = 0;
316  uint32_t microseconds = 0;
317  uint32_t nanoseconds = 0;
318 };
319 
320 template <typename TimeType> void TimeValue::set(Unit unit, TimeType time)
321 {
322  overflow = (time == TimeType(-1));
323  this->unit = unit;
324 
325  TimeType elem[UnitMax + 1] = {0};
326  elem[unit] = time;
327 
328  auto divmod = [&elem](Unit u, uint16_t div) {
329  elem[u + 1] = elem[u] / div;
330  elem[u] %= div;
331  };
332 
333  if(unit < Days) {
334  if(unit < Hours) {
335  if(unit < Minutes) {
336  if(unit < Seconds) {
337  if(unit < Milliseconds) {
338  if(unit < Microseconds) {
339  divmod(Nanoseconds, 1000);
340  }
341  divmod(Microseconds, 1000);
342  }
343  divmod(Milliseconds, 1000);
344  }
345  divmod(Seconds, 60);
346  }
347  divmod(Minutes, 60);
348  }
349  divmod(Hours, 24);
350  }
351 
352  nanoseconds = elem[Nanoseconds];
353  microseconds = elem[Microseconds];
354  milliseconds = elem[Milliseconds];
355  seconds = elem[Seconds];
356  minutes = elem[Minutes];
357  hours = elem[Hours];
358  days = elem[Days];
359 }
360 
364 template <typename T> struct Time {
365  Time() = default;
366 
368  {
369  }
370 
371  void set(Unit unit, T time)
372  {
373  this->unit = unit;
374  this->time = time;
375  }
376 
377  operator T() const
378  {
379  return time;
380  }
381 
382  String toString() const
383  {
384  String s(time);
385  s += unitToString(unit);
386  return s;
387  }
388 
389  friend Time& operator+(Time lhs, const Time& rhs)
390  {
391  lhs.time += rhs.as(lhs.unit);
392  return lhs;
393  }
394 
396  {
397  time += rhs.as(unit);
398  return *this;
399  }
400 
401  TimeValue value() const
402  {
403  return TimeValue(unit, time);
404  }
405 
406  template <Unit unitTo> Time as() const
407  {
408  return Time(unitTo, convert(time, unit, unitTo));
409  }
410 
411  Time as(Unit unitTo) const
412  {
413  return Time(unitTo, convert(time, unit, unitTo));
414  }
415 
417  T time = 0;
418 };
419 
423 template <typename T> Time<T> time(Unit unit, T value)
424 {
425  return Time<T>(unit, value);
426 }
427 
431 template <typename Clock_, typename T> struct Ticks {
432  using Clock = Clock_;
433 
434  static constexpr Clock clock()
435  {
436  return Clock();
437  }
438 
440  {
441  }
442 
443  operator T() const
444  {
445  return ticks;
446  }
447 
448  String toString() const
449  {
450  return String(ticks);
451  }
452 
453  template <Unit unit> Time<T> as()
454  {
455  return Time<T>(unit, Clock::template ticksToTime<unit>(ticks));
456  }
457 
458  T ticks;
459 };
460 
468 template <class Clock_, Unit unit_, uint64_t time_> struct TimeConst {
469  using Clock = Clock_;
470  using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>;
471 
472  static constexpr Clock clock()
473  {
474  return Clock();
475  }
476 
477  static constexpr Unit unit()
478  {
479  return unit_;
480  }
481 
482  static constexpr uint64_t time()
483  {
484  return time_;
485  }
486 
487  constexpr operator uint64_t()
488  {
489  return time_;
490  }
491 
497  {
498  return BasicRatio32{TicksPerUnit::num, TicksPerUnit::den};
499  }
500 
505  static constexpr uint64_t ticks()
506  {
507  return round(double(time_) * TicksPerUnit::num / TicksPerUnit::den);
508  }
509 
514  static constexpr void check()
515  {
516  static_assert(ticks() <= Clock::maxTicks(), "Time exceeds clock range");
517  }
518 
523  static constexpr uint64_t clockTime()
524  {
525  return round(double(ticks()) * TicksPerUnit::den / TicksPerUnit::num);
526  }
527 
533  template <Unit unit> static constexpr uint64_t as()
534  {
535  return convert<time_, unit_, unit>();
536  }
537 
538  static TimeValue value()
539  {
540  return TimeValue(unit_, time_);
541  }
542 
544  {
545  return TimeValue(unit_, clockTime());
546  }
547 
548  static String toString()
549  {
550  return Time<uint64_t>(unit_, time_).toString();
551  }
552 };
553 
560 template <class Clock_, uint64_t ticks_> struct TicksConst {
561  using Clock = Clock_;
562  using TickType = uint64_t;
563  using TimeType = uint64_t;
564 
565  static constexpr TickType ticks()
566  {
567  return ticks_;
568  }
569 
570  constexpr operator TickType()
571  {
572  return ticks_;
573  }
574 
579  static constexpr void check()
580  {
581  static_assert(ticks_ <= Clock::maxTicks(), "Ticks exceeds clock range");
582  }
583 
584  template <Unit unit>
585  using TimeConst = TimeConst<Clock, unit,
586  TimeType(round(double(ticks_) * Clock::template TicksPerUnit<unit>::den /
587  Clock::template TicksPerUnit<unit>::num))>;
588 
594  template <Unit unit> static constexpr TimeConst<unit> as()
595  {
596  return TimeConst<unit>();
597  }
598 
599  static String toString()
600  {
601  return String(ticks_);
602  }
603 };
604 
613 template <Unit unitsFrom, Unit unitsTo, typename TimeType> __forceinline TimeType convert(const TimeType& time)
614 {
615  if(unitsFrom == unitsTo) {
616  return time;
617  }
618 
619  using R = std::ratio_divide<UnitTickRatio<unitsTo>, UnitTickRatio<unitsFrom>>;
620  return muldiv<R::num, R::den>(time);
621 }
622 
630 template <class Clock_, Unit unit_, typename TimeType_> struct TimeSource : public Clock_ {
631  using Clock = Clock_;
632  using TimeType = TimeType_;
633  using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>;
634  template <uint64_t time> using TimeConst = TimeConst<Clock, unit_, time>;
635  template <uint64_t ticks> using TicksConst = TicksConst<Clock, ticks>;
636 
637  static constexpr Unit unit()
638  {
639  return unit_;
640  }
641 
646  static constexpr BasicRatio32 ticksPerUnit()
647  {
648  return {TicksPerUnit::num, TicksPerUnit::den};
649  }
650 
654  using MaxClockTime = typename Clock::template MaxTime<unit_>;
655  static constexpr MaxClockTime maxClockTime()
656  {
657  return MaxClockTime();
658  }
659 
660  // Check against arbitrary minimum - could be 1, but is that actually useful?
661  static_assert(maxClockTime() >= 5, "Time units too large for Clock ");
662 
669  template <uint64_t time> static constexpr TimeConst<time> timeConst()
670  {
671  return TimeConst<time>();
672  }
673 
680  template <uint64_t ticks> static constexpr TicksConst<ticks> ticksConst()
681  {
682  return TicksConst<ticks>();
683  }
684 
689  static constexpr Time<TimeType_> maxCalcTime()
690  {
692  }
693 
699  {
701  }
702 
709  {
710  return muldiv<TicksPerUnit::num, TicksPerUnit::den>(time);
711  }
712 
718  template <uint64_t time> static constexpr uint64_t timeToTicks()
719  {
720  return TimeConst<time>::ticks();
721  }
722 
728  template <uint64_t ticks> static constexpr uint64_t ticksToTime()
729  {
730  return TicksConst<ticks>::template as<unit>();
731  }
732 
739  {
740  return Time<TimeType_>(unit_, muldiv<TicksPerUnit::den, TicksPerUnit::num>(ticks));
741  }
742 
743  static String toString()
744  {
745  String s;
746  s += Clock::toString();
747  s += '/';
748  s += sizeof(TimeType) * 8;
749  s += "-bit/";
750  s += unitToLongString(unit_);
751  return s;
752  }
753 };
754 
755 } // namespace NanoTime
756 
#define round(x)
Definition: ArduinoCompat.h:23
The String class.
Definition: WString.h:137
Ratio< uint32_t > Ratio32
Definition: Rational.h:157
Definition: NanoTime.h:40
constexpr uint64_t convert()
Function template to convert a constant time quantity from one unit to another.
Definition: NanoTime.h:243
constexpr BasicRatio32 unitTicks[UnitMax+1]
List of clock ticks for each supported unit of time.
Definition: NanoTime.h:96
const char * unitToLongString(Unit unit)
Get a long string identifying the given time units, e.g. "seconds".
const char * unitToString(Unit unit)
Get a string identifying the given time units, e.g. "ns".
Time< T > time(Unit unit, T value)
Helper function to create a Time and deduce the type.
Definition: NanoTime.h:423
std::ratio< unitTicks[unit].num, unitTicks[unit].den > UnitTickRatio
Class template to define tick std::ratio type.
Definition: NanoTime.h:111
Unit
Identify units for a scalar quantity of time.
Definition: NanoTime.h:45
@ UnitMax
Definition: NanoTime.h:53
@ Hours
Definition: NanoTime.h:51
@ Microseconds
Definition: NanoTime.h:47
@ Seconds
Definition: NanoTime.h:49
@ Minutes
Definition: NanoTime.h:50
@ Milliseconds
Definition: NanoTime.h:48
@ Nanoseconds
Definition: NanoTime.h:46
@ Days
Definition: NanoTime.h:52
A basic rational fraction, constexpr-compatible.
Definition: Rational.h:37
T num
Definition: Rational.h:38
T den
Definition: Rational.h:39
Obtain limits for a muldiv template calculation.
Definition: muldiv.h:60
static constexpr ValType maxValue()
Get the maximum value which can be used for a muldiv calculation without overflowing.
Definition: muldiv.h:75
Class template representing a physical Clock with fixed timing characteristics.
Definition: NanoTime.h:131
static constexpr TicksConst< ticks > ticksConst()
Class template defining a fixed tick quantity.
Definition: NanoTime.h:186
TimeSource< Clock, unit, TimeType > TimeSource
Definition: NanoTime.h:136
static constexpr uint32_t frequency()
Definition: NanoTime.h:144
static constexpr MaxTicks maxTicks()
Definition: NanoTime.h:150
static Ratio32 ticksPerUnit(Unit unit)
Get ticks per unit as a Ratio object.
Definition: NanoTime.h:165
Ticks< Clock, T > Ticks
Definition: NanoTime.h:137
static Ticks< TimeType > timeToTicks(TimeType time)
Get the number of ticks for a given time.
Definition: NanoTime.h:206
static constexpr const char * typeName()
Definition: NanoTime.h:139
typename MaxTicks::template TimeConst< unit > MaxTime
Definition: NanoTime.h:155
TicksConst< maxTicks_ > MaxTicks
Definition: NanoTime.h:149
static constexpr TimeConst< unit, time > timeConst()
Class template defining a fixed time quantity.
Definition: NanoTime.h:176
TimeConst< Clock, unit, time > TimeConst
Definition: NanoTime.h:134
static constexpr TimeSource< unit, TimeType > timeSource()
Create a Time Source for this Clock.
Definition: NanoTime.h:196
static String toString()
Definition: NanoTime.h:221
static Time< TimeType > ticksToTime(TimeType ticks)
Get the time for a given number of clock ticks.
Definition: NanoTime.h:216
static constexpr MaxTime< unit > maxTime()
Definition: NanoTime.h:156
std::ratio_divide< std::ratio< frequency_ >, UnitTickRatio< unit > > TicksPerUnit
Definition: NanoTime.h:135
TicksConst< Clock, ticks > TicksConst
Definition: NanoTime.h:133
TickType_ TickType
Definition: NanoTime.h:132
Class to represent a frequency.
Definition: NanoTime.h:69
Frequency(uint32_t frequency)
Definition: NanoTime.h:70
String toString() const
Get frequency as compact string.
uint32_t frequency
Definition: NanoTime.h:90
Class template representing a fixed clock tick count.
Definition: NanoTime.h:560
static constexpr void check()
Obtain the tick count with a static range check against Clock maxTicks.
Definition: NanoTime.h:579
uint64_t TickType
Definition: NanoTime.h:562
uint64_t TimeType
Definition: NanoTime.h:563
static constexpr TickType ticks()
Definition: NanoTime.h:565
Clock_ Clock
Definition: NanoTime.h:561
static String toString()
Definition: NanoTime.h:599
TimeConst< Clock, unit, TimeType(round(double(ticks_) *Clock::template TicksPerUnit< unit >::den/Clock::template TicksPerUnit< unit >::num))> TimeConst
Definition: NanoTime.h:587
static constexpr TimeConst< unit > as()
Get the time for the tick count in a specific time unit.
Definition: NanoTime.h:594
Class to handle a tick value associated with a clock.
Definition: NanoTime.h:431
Time< T > as()
Definition: NanoTime.h:453
static constexpr Clock clock()
Definition: NanoTime.h:434
String toString() const
Definition: NanoTime.h:448
Ticks(T ticks)
Definition: NanoTime.h:439
T ticks
Definition: NanoTime.h:458
Clock_ Clock
Definition: NanoTime.h:432
Class template to represent a fixed time value for a specific Clock.
Definition: NanoTime.h:468
static constexpr uint64_t time()
Definition: NanoTime.h:482
static constexpr uint64_t ticks()
Return the corresponding tick value for the time interval.
Definition: NanoTime.h:505
static TimeValue value()
Definition: NanoTime.h:538
typename Clock::template TicksPerUnit< unit_ > TicksPerUnit
Definition: NanoTime.h:470
static constexpr Unit unit()
Definition: NanoTime.h:477
static constexpr uint64_t as()
Obtain the time in a different set of units.
Definition: NanoTime.h:533
static TimeValue clockValue()
Definition: NanoTime.h:543
static constexpr uint64_t clockTime()
Obtain the actual Clock time by converting tick value.
Definition: NanoTime.h:523
static constexpr void check()
Use this function to perform a static (compile-time) range check against Clock maxTicks.
Definition: NanoTime.h:514
Clock_ Clock
Definition: NanoTime.h:469
static constexpr Clock clock()
Definition: NanoTime.h:472
static String toString()
Definition: NanoTime.h:548
static Ratio32 ticksPerUnit()
Get ticks per unit as a Ratio object.
Definition: NanoTime.h:496
Class template for accessing a Clock in specific time units.
Definition: NanoTime.h:630
static Time< TimeType_ > ticksToTime(TimeType ticks)
Get the time for a given number of clock ticks.
Definition: NanoTime.h:738
static constexpr uint64_t ticksToTime()
Get the time for a given number of clock ticks.
Definition: NanoTime.h:728
TicksConst< Clock, ticks > TicksConst
Definition: NanoTime.h:635
static constexpr Unit unit()
Definition: NanoTime.h:637
static constexpr uint64_t timeToTicks()
Get the number of ticks for a given time.
Definition: NanoTime.h:718
static constexpr MaxClockTime maxClockTime()
Definition: NanoTime.h:655
typename Clock::template TicksPerUnit< unit_ > TicksPerUnit
Definition: NanoTime.h:633
TimeConst< Clock, unit_, time > TimeConst
Definition: NanoTime.h:634
static String toString()
Definition: NanoTime.h:743
static constexpr Ticks< Clock_, TimeType_ > maxCalcTicks()
The maximum tick value supported by ticksToTime without overflowing.
Definition: NanoTime.h:698
static constexpr Time< TimeType_ > maxCalcTime()
The maximum time value supported by timeToTicks without overflowing.
Definition: NanoTime.h:689
static constexpr TimeConst< time > timeConst()
Obtain a TimeConst type representing the given time quantity.
Definition: NanoTime.h:669
TimeType_ TimeType
Definition: NanoTime.h:632
static constexpr TicksConst< ticks > ticksConst()
Class template defining a fixed tick quantity.
Definition: NanoTime.h:680
static Ticks< Clock_, TimeType_ > timeToTicks(TimeType time)
Get the number of ticks for a given time.
Definition: NanoTime.h:708
typename Clock::template MaxTime< unit_ > MaxClockTime
Get the time corresponding to the maximum clock tick value.
Definition: NanoTime.h:654
static constexpr BasicRatio32 ticksPerUnit()
Number of clock ticks per unit of time.
Definition: NanoTime.h:646
A time time broken into its constituent elements.
Definition: NanoTime.h:271
uint8_t hours
Definition: NanoTime.h:312
TimeValue(Unit unit, TimeType time)
Resolve a time value into constituent components.
Definition: NanoTime.h:279
uint32_t nanoseconds
Definition: NanoTime.h:317
String toString() const
uint16_t milliseconds
Definition: NanoTime.h:315
uint32_t getNanoseconds() const
Get sub-second time entirely in nanoseconds.
Definition: NanoTime.h:297
uint8_t minutes
Definition: NanoTime.h:313
void set(Unit unit, TimeType time)
Definition: NanoTime.h:320
uint32_t getMicroseconds() const
Get sub-second time entirely in microseconds.
Definition: NanoTime.h:289
bool overflow
Definition: NanoTime.h:309
uint32_t days
Definition: NanoTime.h:311
Unit unit
Time unit passed to set() call.
Definition: NanoTime.h:310
uint32_t microseconds
Definition: NanoTime.h:316
uint8_t seconds
Definition: NanoTime.h:314
Class to handle a simple time value with associated unit.
Definition: NanoTime.h:364
Unit unit
Definition: NanoTime.h:416
friend Time & operator+(Time lhs, const Time &rhs)
Definition: NanoTime.h:389
Time as() const
Definition: NanoTime.h:406
Time & operator+=(Time< T > rhs)
Definition: NanoTime.h:395
TimeValue value() const
Definition: NanoTime.h:401
Time()=default
void set(Unit unit, T time)
Definition: NanoTime.h:371
T time
Definition: NanoTime.h:417
String toString() const
Definition: NanoTime.h:382
Time as(Unit unitTo) const
Definition: NanoTime.h:411
Time(Unit unit, T time)
Definition: NanoTime.h:367
Class to simplify calculations of finite rationals at runtime.
Definition: Rational.h:65