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 {
41 constexpr uint64_t round_ce(double v)
42 {
43 #ifdef __clang__
44  return uint64_t(v + 0.5);
45 #else
46  return round(v);
47 #endif
48 }
49 
54 enum Unit {
63 };
64 
68 const char* unitToString(Unit unit);
69 
73 const char* unitToLongString(Unit unit);
74 
78 struct Frequency {
80  {
81  }
82 
83  operator uint32_t()
84  {
85  return frequency;
86  }
87 
97  String toString() const;
98 
99  uint32_t frequency;
100 };
101 
105 constexpr BasicRatio32 unitTicks[UnitMax + 1] = {
106  {1000000000, 1}, // Nanoseconds
107  {1000000, 1}, // Microseconds
108  {1000, 1}, // Milliseconds
109  {1, 1}, // Seconds
110  {1, 60}, // Minutes
111  {1, 60 * 60}, // Hours
112  {1, 24 * 60 * 60}, // Days
113 };
114 
120 template <Unit unit> using UnitTickRatio = std::ratio<unitTicks[unit].num, unitTicks[unit].den>;
121 
122 // Forward declarations
123 template <class Clock_, Unit unit_, uint64_t time_> struct TimeConst;
124 template <class Clock_, uint64_t ticks_> struct TicksConst;
125 template <class Clock_, Unit unit_, typename TimeType_> struct TimeSource;
126 template <typename T> struct Time;
127 template <typename Clock_, typename T> struct Ticks;
128 
140 template <typename ClockDef, uint32_t frequency_, typename TickType_, TickType_ maxTicks_> struct Clock {
141  using TickType = TickType_;
142  template <uint64_t ticks> using TicksConst = TicksConst<Clock, ticks>;
143  template <Unit unit, uint64_t time> using TimeConst = TimeConst<Clock, unit, time>;
144  template <Unit unit> using TicksPerUnit = std::ratio_divide<std::ratio<frequency_>, UnitTickRatio<unit>>;
145  template <Unit unit, typename TimeType> using TimeSource = TimeSource<Clock, unit, TimeType>;
146  template <typename T> using Ticks = Ticks<Clock, T>;
147 
148  static constexpr const char* typeName()
149  {
150  return ClockDef::typeName();
151  }
152 
153  static constexpr uint32_t frequency()
154  {
155  return frequency_;
156  }
157 
159  static constexpr MaxTicks maxTicks()
160  {
161  return MaxTicks();
162  }
163 
164  template <Unit unit> using MaxTime = typename MaxTicks::template TimeConst<unit>;
165  template <Unit unit> static constexpr MaxTime<unit> maxTime()
166  {
167  return MaxTime<unit>();
168  }
169 
174  static Ratio32 ticksPerUnit(Unit unit)
175  {
176  const auto& tpu = unitTicks[unit];
177  return Ratio32(frequency_ * tpu.den, tpu.num);
178  }
179 
185  template <Unit unit, uint64_t time> static constexpr TimeConst<unit, time> timeConst()
186  {
187  return TimeConst<unit, time>();
188  }
189 
195  template <uint64_t ticks> static constexpr TicksConst<ticks> ticksConst()
196  {
197  return TicksConst<ticks>();
198  }
199 
205  template <Unit unit, typename TimeType> static constexpr TimeSource<unit, TimeType> timeSource()
206  {
208  }
209 
215  template <Unit unit, typename TimeType> static Ticks<TimeType> timeToTicks(TimeType time)
216  {
218  }
219 
225  template <Unit unit, typename TimeType> static Time<TimeType> ticksToTime(TimeType ticks)
226  {
228  }
229 
230  static String toString()
231  {
232  String s;
233  s += typeName();
234  s += '/';
235  s += Frequency(frequency()).toString();
236  return s;
237  }
238 };
239 
250 template <uint64_t time, Unit unitsFrom, Unit unitsTo,
251  typename R = std::ratio_divide<UnitTickRatio<unitsTo>, UnitTickRatio<unitsFrom>>>
252 constexpr uint64_t convert()
253 {
254  return ({ round_ce(double(time) * R::num / R::den); });
255 }
256 
265 template <typename TimeType> __forceinline TimeType convert(const TimeType& time, Unit unitsFrom, Unit unitsTo)
266 {
267  if(unitsFrom == unitsTo) {
268  return time;
269  }
270 
271  using R = Ratio<TimeType>;
272  auto ratio = R(unitTicks[unitsTo]) / R(unitTicks[unitsFrom]);
273  return time * ratio;
274 }
275 
280 struct TimeValue {
281  TimeValue() = default;
282 
288  template <typename TimeType> TimeValue(Unit unit, TimeType time)
289  {
290  set(unit, time);
291  }
292 
293  template <typename TimeType> void set(Unit unit, TimeType time);
294 
298  uint32_t getMicroseconds() const
299  {
300  return microseconds + (milliseconds * 1000);
301  }
302 
306  uint32_t getNanoseconds() const
307  {
308  return nanoseconds + getMicroseconds() * 1000;
309  }
310 
311  String toString() const;
312 
313  operator String() const
314  {
315  return toString();
316  }
317 
318  bool overflow = false;
320  uint32_t days = 0;
321  uint8_t hours = 0;
322  uint8_t minutes = 0;
323  uint8_t seconds = 0;
324  uint16_t milliseconds = 0;
325  uint32_t microseconds = 0;
326  uint32_t nanoseconds = 0;
327 };
328 
329 template <typename TimeType> void TimeValue::set(Unit unit, TimeType time)
330 {
331  overflow = (time == TimeType(-1));
332  this->unit = unit;
333 
334  TimeType elem[UnitMax + 1] = {0};
335  elem[unit] = time;
336 
337  auto divmod = [&elem](Unit u, uint16_t div) {
338  elem[u + 1] = elem[u] / div;
339  elem[u] %= div;
340  };
341 
342  if(unit < Days) {
343  if(unit < Hours) {
344  if(unit < Minutes) {
345  if(unit < Seconds) {
346  if(unit < Milliseconds) {
347  if(unit < Microseconds) {
348  divmod(Nanoseconds, 1000);
349  }
350  divmod(Microseconds, 1000);
351  }
352  divmod(Milliseconds, 1000);
353  }
354  divmod(Seconds, 60);
355  }
356  divmod(Minutes, 60);
357  }
358  divmod(Hours, 24);
359  }
360 
361  nanoseconds = elem[Nanoseconds];
362  microseconds = elem[Microseconds];
363  milliseconds = elem[Milliseconds];
364  seconds = elem[Seconds];
365  minutes = elem[Minutes];
366  hours = elem[Hours];
367  days = elem[Days];
368 }
369 
373 template <typename T> struct Time {
374  Time() = default;
375 
377  {
378  }
379 
380  void set(Unit unit, T time)
381  {
382  this->unit = unit;
383  this->time = time;
384  }
385 
386  operator T() const
387  {
388  return time;
389  }
390 
391  String toString() const
392  {
393  String s(time);
394  s += unitToString(unit);
395  return s;
396  }
397 
398  friend Time& operator+(Time lhs, const Time& rhs)
399  {
400  lhs.time += rhs.as(lhs.unit);
401  return lhs;
402  }
403 
405  {
406  time += rhs.as(unit);
407  return *this;
408  }
409 
410  TimeValue value() const
411  {
412  return TimeValue(unit, time);
413  }
414 
415  template <Unit unitTo> Time as() const
416  {
417  return Time(unitTo, convert(time, unit, unitTo));
418  }
419 
420  Time as(Unit unitTo) const
421  {
422  return Time(unitTo, convert(time, unit, unitTo));
423  }
424 
426  T time = 0;
427 };
428 
432 template <typename T> Time<T> time(Unit unit, T value)
433 {
434  return Time<T>(unit, value);
435 }
436 
440 template <typename Clock_, typename T> struct Ticks {
441  using Clock = Clock_;
442 
443  static constexpr Clock clock()
444  {
445  return Clock();
446  }
447 
449  {
450  }
451 
452  operator T() const
453  {
454  return ticks;
455  }
456 
457  String toString() const
458  {
459  return String(ticks);
460  }
461 
462  template <Unit unit> Time<T> as()
463  {
464  return Time<T>(unit, Clock::template ticksToTime<unit>(ticks));
465  }
466 
467  T ticks;
468 };
469 
477 template <class Clock_, Unit unit_, uint64_t time_> struct TimeConst {
478  using Clock = Clock_;
479  using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>;
480 
481  static constexpr Clock clock()
482  {
483  return Clock();
484  }
485 
486  static constexpr Unit unit()
487  {
488  return unit_;
489  }
490 
491  static constexpr uint64_t time()
492  {
493  return time_;
494  }
495 
496  constexpr operator uint64_t()
497  {
498  return time_;
499  }
500 
506  {
507  return BasicRatio32{TicksPerUnit::num, TicksPerUnit::den};
508  }
509 
514  static constexpr uint64_t ticks()
515  {
516  return round_ce(double(time_) * TicksPerUnit::num / TicksPerUnit::den);
517  }
518 
523  static constexpr void check()
524  {
525  static_assert(ticks() <= Clock::maxTicks(), "Time exceeds clock range");
526  }
527 
532  static constexpr uint64_t clockTime()
533  {
534  return round_ce(double(ticks()) * TicksPerUnit::den / TicksPerUnit::num);
535  }
536 
542  template <Unit unit> static constexpr uint64_t as()
543  {
544  return convert<time_, unit_, unit>();
545  }
546 
547  static TimeValue value()
548  {
549  return TimeValue(unit_, time_);
550  }
551 
553  {
554  return TimeValue(unit_, clockTime());
555  }
556 
557  static String toString()
558  {
559  return Time<uint64_t>(unit_, time_).toString();
560  }
561 };
562 
569 template <class Clock_, uint64_t ticks_> struct TicksConst {
570  using Clock = Clock_;
571  using TickType = uint64_t;
572  using TimeType = uint64_t;
573 
574  static constexpr TickType ticks()
575  {
576  return ticks_;
577  }
578 
579  constexpr operator TickType()
580  {
581  return ticks_;
582  }
583 
588  static constexpr void check()
589  {
590  static_assert(ticks_ <= Clock::maxTicks(), "Ticks exceeds clock range");
591  }
592 
593  template <Unit unit>
594  using TimeConst = TimeConst<Clock, unit,
595  TimeType(round_ce(double(ticks_) * Clock::template TicksPerUnit<unit>::den /
596  Clock::template TicksPerUnit<unit>::num))>;
597 
603  template <Unit unit> static constexpr TimeConst<unit> as()
604  {
605  return TimeConst<unit>();
606  }
607 
608  static String toString()
609  {
610  return String(ticks_);
611  }
612 };
613 
622 template <Unit unitsFrom, Unit unitsTo, typename TimeType> __forceinline TimeType convert(const TimeType& time)
623 {
624  if(unitsFrom == unitsTo) {
625  return time;
626  }
627 
628  using R = std::ratio_divide<UnitTickRatio<unitsTo>, UnitTickRatio<unitsFrom>>;
629  return muldiv<R::num, R::den>(time);
630 }
631 
639 template <class Clock_, Unit unit_, typename TimeType_> struct TimeSource : public Clock_ {
640  using Clock = Clock_;
641  using TimeType = TimeType_;
642  using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>;
643  template <uint64_t time> using TimeConst = TimeConst<Clock, unit_, time>;
644  template <uint64_t ticks> using TicksConst = TicksConst<Clock, ticks>;
645 
646  static constexpr Unit unit()
647  {
648  return unit_;
649  }
650 
655  static constexpr BasicRatio32 ticksPerUnit()
656  {
657  return {TicksPerUnit::num, TicksPerUnit::den};
658  }
659 
663  using MaxClockTime = typename Clock::template MaxTime<unit_>;
664  static constexpr MaxClockTime maxClockTime()
665  {
666  return MaxClockTime();
667  }
668 
669  // Check against arbitrary minimum - could be 1, but is that actually useful?
670  static_assert(maxClockTime() >= 5, "Time units too large for Clock ");
671 
678  template <uint64_t time> static constexpr TimeConst<time> timeConst()
679  {
680  return TimeConst<time>();
681  }
682 
689  template <uint64_t ticks> static constexpr TicksConst<ticks> ticksConst()
690  {
691  return TicksConst<ticks>();
692  }
693 
698  static constexpr Time<TimeType_> maxCalcTime()
699  {
701  }
702 
708  {
710  }
711 
718  {
719  return muldiv<TicksPerUnit::num, TicksPerUnit::den>(time);
720  }
721 
727  template <uint64_t time> static constexpr uint64_t timeToTicks()
728  {
729  return TimeConst<time>::ticks();
730  }
731 
737  template <uint64_t ticks> static constexpr uint64_t ticksToTime()
738  {
739  return TicksConst<ticks>::template as<unit>();
740  }
741 
748  {
749  return Time<TimeType_>(unit_, muldiv<TicksPerUnit::den, TicksPerUnit::num>(ticks));
750  }
751 
752  static String toString()
753  {
754  String s;
755  s += Clock::toString();
756  s += '/';
757  s += sizeof(TimeType) * 8;
758  s += "-bit/";
759  s += unitToLongString(unit_);
760  return s;
761  }
762 };
763 
764 } // namespace NanoTime
765 
#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:252
constexpr BasicRatio32 unitTicks[UnitMax+1]
List of clock ticks for each supported unit of time.
Definition: NanoTime.h:105
constexpr uint64_t round_ce(double v)
Definition: NanoTime.h:41
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:432
std::ratio< unitTicks[unit].num, unitTicks[unit].den > UnitTickRatio
Class template to define tick std::ratio type.
Definition: NanoTime.h:120
Unit
Identify units for a scalar quantity of time.
Definition: NanoTime.h:54
@ UnitMax
Definition: NanoTime.h:62
@ Hours
Definition: NanoTime.h:60
@ Microseconds
Definition: NanoTime.h:56
@ Seconds
Definition: NanoTime.h:58
@ Minutes
Definition: NanoTime.h:59
@ Milliseconds
Definition: NanoTime.h:57
@ Nanoseconds
Definition: NanoTime.h:55
@ Days
Definition: NanoTime.h:61
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:140
static constexpr TicksConst< ticks > ticksConst()
Class template defining a fixed tick quantity.
Definition: NanoTime.h:195
TimeSource< Clock, unit, TimeType > TimeSource
Definition: NanoTime.h:145
static constexpr uint32_t frequency()
Definition: NanoTime.h:153
static constexpr MaxTicks maxTicks()
Definition: NanoTime.h:159
static Ratio32 ticksPerUnit(Unit unit)
Get ticks per unit as a Ratio object.
Definition: NanoTime.h:174
Ticks< Clock, T > Ticks
Definition: NanoTime.h:146
static Ticks< TimeType > timeToTicks(TimeType time)
Get the number of ticks for a given time.
Definition: NanoTime.h:215
static constexpr const char * typeName()
Definition: NanoTime.h:148
typename MaxTicks::template TimeConst< unit > MaxTime
Definition: NanoTime.h:164
TicksConst< maxTicks_ > MaxTicks
Definition: NanoTime.h:158
static constexpr TimeConst< unit, time > timeConst()
Class template defining a fixed time quantity.
Definition: NanoTime.h:185
TimeConst< Clock, unit, time > TimeConst
Definition: NanoTime.h:143
static constexpr TimeSource< unit, TimeType > timeSource()
Create a Time Source for this Clock.
Definition: NanoTime.h:205
static String toString()
Definition: NanoTime.h:230
static Time< TimeType > ticksToTime(TimeType ticks)
Get the time for a given number of clock ticks.
Definition: NanoTime.h:225
static constexpr MaxTime< unit > maxTime()
Definition: NanoTime.h:165
std::ratio_divide< std::ratio< frequency_ >, UnitTickRatio< unit > > TicksPerUnit
Definition: NanoTime.h:144
TicksConst< Clock, ticks > TicksConst
Definition: NanoTime.h:142
TickType_ TickType
Definition: NanoTime.h:141
Class to represent a frequency.
Definition: NanoTime.h:78
Frequency(uint32_t frequency)
Definition: NanoTime.h:79
String toString() const
Get frequency as compact string.
uint32_t frequency
Definition: NanoTime.h:99
Class template representing a fixed clock tick count.
Definition: NanoTime.h:569
static constexpr void check()
Obtain the tick count with a static range check against Clock maxTicks.
Definition: NanoTime.h:588
uint64_t TickType
Definition: NanoTime.h:571
TimeConst< Clock, unit, TimeType(round_ce(double(ticks_) *Clock::template TicksPerUnit< unit >::den/Clock::template TicksPerUnit< unit >::num))> TimeConst
Definition: NanoTime.h:596
uint64_t TimeType
Definition: NanoTime.h:572
static constexpr TickType ticks()
Definition: NanoTime.h:574
Clock_ Clock
Definition: NanoTime.h:570
static String toString()
Definition: NanoTime.h:608
static constexpr TimeConst< unit > as()
Get the time for the tick count in a specific time unit.
Definition: NanoTime.h:603
Class to handle a tick value associated with a clock.
Definition: NanoTime.h:440
Time< T > as()
Definition: NanoTime.h:462
static constexpr Clock clock()
Definition: NanoTime.h:443
String toString() const
Definition: NanoTime.h:457
Ticks(T ticks)
Definition: NanoTime.h:448
T ticks
Definition: NanoTime.h:467
Clock_ Clock
Definition: NanoTime.h:441
Class template to represent a fixed time value for a specific Clock.
Definition: NanoTime.h:477
static constexpr uint64_t time()
Definition: NanoTime.h:491
static constexpr uint64_t ticks()
Return the corresponding tick value for the time interval.
Definition: NanoTime.h:514
static TimeValue value()
Definition: NanoTime.h:547
typename Clock::template TicksPerUnit< unit_ > TicksPerUnit
Definition: NanoTime.h:479
static constexpr Unit unit()
Definition: NanoTime.h:486
static constexpr uint64_t as()
Obtain the time in a different set of units.
Definition: NanoTime.h:542
static TimeValue clockValue()
Definition: NanoTime.h:552
static constexpr uint64_t clockTime()
Obtain the actual Clock time by converting tick value.
Definition: NanoTime.h:532
static constexpr void check()
Use this function to perform a static (compile-time) range check against Clock maxTicks.
Definition: NanoTime.h:523
Clock_ Clock
Definition: NanoTime.h:478
static constexpr Clock clock()
Definition: NanoTime.h:481
static String toString()
Definition: NanoTime.h:557
static Ratio32 ticksPerUnit()
Get ticks per unit as a Ratio object.
Definition: NanoTime.h:505
Class template for accessing a Clock in specific time units.
Definition: NanoTime.h:639
static Time< TimeType_ > ticksToTime(TimeType ticks)
Get the time for a given number of clock ticks.
Definition: NanoTime.h:747
static constexpr uint64_t ticksToTime()
Get the time for a given number of clock ticks.
Definition: NanoTime.h:737
TicksConst< Clock, ticks > TicksConst
Definition: NanoTime.h:644
static constexpr Unit unit()
Definition: NanoTime.h:646
static constexpr uint64_t timeToTicks()
Get the number of ticks for a given time.
Definition: NanoTime.h:727
static constexpr MaxClockTime maxClockTime()
Definition: NanoTime.h:664
typename Clock::template TicksPerUnit< unit_ > TicksPerUnit
Definition: NanoTime.h:642
TimeConst< Clock, unit_, time > TimeConst
Definition: NanoTime.h:643
static String toString()
Definition: NanoTime.h:752
static constexpr Ticks< Clock_, TimeType_ > maxCalcTicks()
The maximum tick value supported by ticksToTime without overflowing.
Definition: NanoTime.h:707
static constexpr Time< TimeType_ > maxCalcTime()
The maximum time value supported by timeToTicks without overflowing.
Definition: NanoTime.h:698
static constexpr TimeConst< time > timeConst()
Obtain a TimeConst type representing the given time quantity.
Definition: NanoTime.h:678
TimeType_ TimeType
Definition: NanoTime.h:641
static constexpr TicksConst< ticks > ticksConst()
Class template defining a fixed tick quantity.
Definition: NanoTime.h:689
static Ticks< Clock_, TimeType_ > timeToTicks(TimeType time)
Get the number of ticks for a given time.
Definition: NanoTime.h:717
typename Clock::template MaxTime< unit_ > MaxClockTime
Get the time corresponding to the maximum clock tick value.
Definition: NanoTime.h:663
static constexpr BasicRatio32 ticksPerUnit()
Number of clock ticks per unit of time.
Definition: NanoTime.h:655
A time time broken into its constituent elements.
Definition: NanoTime.h:280
uint8_t hours
Definition: NanoTime.h:321
TimeValue(Unit unit, TimeType time)
Resolve a time value into constituent components.
Definition: NanoTime.h:288
uint32_t nanoseconds
Definition: NanoTime.h:326
String toString() const
uint16_t milliseconds
Definition: NanoTime.h:324
uint32_t getNanoseconds() const
Get sub-second time entirely in nanoseconds.
Definition: NanoTime.h:306
uint8_t minutes
Definition: NanoTime.h:322
void set(Unit unit, TimeType time)
Definition: NanoTime.h:329
uint32_t getMicroseconds() const
Get sub-second time entirely in microseconds.
Definition: NanoTime.h:298
bool overflow
Definition: NanoTime.h:318
uint32_t days
Definition: NanoTime.h:320
Unit unit
Time unit passed to set() call.
Definition: NanoTime.h:319
uint32_t microseconds
Definition: NanoTime.h:325
uint8_t seconds
Definition: NanoTime.h:323
Class to handle a simple time value with associated unit.
Definition: NanoTime.h:373
Unit unit
Definition: NanoTime.h:425
friend Time & operator+(Time lhs, const Time &rhs)
Definition: NanoTime.h:398
Time as() const
Definition: NanoTime.h:415
Time & operator+=(Time< T > rhs)
Definition: NanoTime.h:404
TimeValue value() const
Definition: NanoTime.h:410
Time()=default
void set(Unit unit, T time)
Definition: NanoTime.h:380
T time
Definition: NanoTime.h:426
String toString() const
Definition: NanoTime.h:391
Time as(Unit unitTo) const
Definition: NanoTime.h:420
Time(Unit unit, T time)
Definition: NanoTime.h:376
Class to simplify calculations of finite rationals at runtime.
Definition: Rational.h:65