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 {
70  Frequency(uint32_t frequency) : frequency(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 
114 template <Unit unit> struct UnitTickRatio {
115  static constexpr uint64_t num = unitTicks[unit].num;
116  static constexpr uint64_t den = unitTicks[unit].den;
117 };
118 
119 // Forward declarations
120 template <class Clock_, Unit unit_, uint64_t time_> struct TimeConst;
121 template <class Clock_, uint64_t ticks_> struct TicksConst;
122 template <class Clock_, Unit unit_, typename TimeType_> struct TimeSource;
123 template <typename T> struct Time;
124 template <typename Clock_, typename T> struct Ticks;
125 
137 template <typename ClockDef, uint32_t frequency_, typename TickType_, TickType_ maxTicks_> struct Clock {
138  using TickType = TickType_;
139  template <uint64_t ticks> using TicksConst = TicksConst<Clock, ticks>;
140  template <Unit unit, uint64_t time> using TimeConst = TimeConst<Clock, unit, time>;
141  template <Unit unit> using TicksPerUnit = std::ratio_divide<std::ratio<frequency_>, UnitTickRatio<unit>>;
142  template <Unit unit, typename TimeType> using TimeSource = TimeSource<Clock, unit, TimeType>;
143  template <typename T> using Ticks = Ticks<Clock, T>;
144 
145  static constexpr const char* typeName()
146  {
147  return ClockDef::typeName();
148  }
149 
150  static constexpr uint32_t frequency()
151  {
152  return frequency_;
153  }
154 
156  static constexpr MaxTicks maxTicks()
157  {
158  return MaxTicks();
159  }
160 
161  template <Unit unit> using MaxTime = typename MaxTicks::template TimeConst<unit>;
162  template <Unit unit> static constexpr MaxTime<unit> maxTime()
163  {
164  return MaxTime<unit>();
165  }
166 
171  static Ratio32 ticksPerUnit(Unit unit)
172  {
173  const auto& tpu = unitTicks[unit];
174  return Ratio32(frequency_ * tpu.den, tpu.num);
175  }
176 
182  template <Unit unit, uint64_t time> static constexpr TimeConst<unit, time> timeConst()
183  {
184  return TimeConst<unit, time>();
185  }
186 
192  template <uint64_t ticks> static constexpr TicksConst<ticks> ticksConst()
193  {
194  return TicksConst<ticks>();
195  }
196 
202  template <Unit unit, typename TimeType> static constexpr TimeSource<unit, TimeType> timeSource()
203  {
205  }
206 
212  template <Unit unit, typename TimeType> static Ticks<TimeType> timeToTicks(TimeType time)
213  {
215  }
216 
222  template <Unit unit, typename TimeType> static Time<TimeType> ticksToTime(TimeType ticks)
223  {
225  }
226 
227  static String toString()
228  {
229  String s;
230  s += typeName();
231  s += '/';
232  s += Frequency(frequency()).toString();
233  return s;
234  }
235 };
236 
247 template <uint64_t time, Unit unitsFrom, Unit unitsTo,
248  typename R = std::ratio_divide<UnitTickRatio<unitsTo>, UnitTickRatio<unitsFrom>>>
249 constexpr uint64_t convert()
250 {
251  return ({ round(double(time) * R::num / R::den); });
252 }
253 
262 template <typename TimeType> __forceinline TimeType convert(const TimeType& time, Unit unitsFrom, Unit unitsTo)
263 {
264  if(unitsFrom == unitsTo) {
265  return time;
266  }
267 
268  using R = Ratio<TimeType>;
269  auto ratio = R(unitTicks[unitsTo]) / R(unitTicks[unitsFrom]);
270  return time * ratio;
271 }
272 
277 struct TimeValue {
278  TimeValue() = default;
279 
285  template <typename TimeType> TimeValue(Unit unit, TimeType time)
286  {
287  set(unit, time);
288  }
289 
290  template <typename TimeType> void set(Unit unit, TimeType time);
291 
295  uint32_t getMicroseconds() const
296  {
297  return microseconds + (milliseconds * 1000);
298  }
299 
303  uint32_t getNanoseconds() const
304  {
305  return nanoseconds + getMicroseconds() * 1000;
306  }
307 
308  String toString() const;
309 
310  operator String() const
311  {
312  return toString();
313  }
314 
315  bool overflow = false;
316  Unit unit = Seconds;
317  uint32_t days = 0;
318  uint8_t hours = 0;
319  uint8_t minutes = 0;
320  uint8_t seconds = 0;
321  uint16_t milliseconds = 0;
322  uint32_t microseconds = 0;
323  uint32_t nanoseconds = 0;
324 };
325 
326 template <typename TimeType> void TimeValue::set(Unit unit, TimeType time)
327 {
328  overflow = (time == TimeType(-1));
329  this->unit = unit;
330 
331  TimeType elem[UnitMax + 1] = {0};
332  elem[unit] = time;
333 
334  auto divmod = [&elem](Unit u, uint16_t div) {
335  elem[u + 1] = elem[u] / div;
336  elem[u] %= div;
337  };
338 
339  if(unit < Days) {
340  if(unit < Hours) {
341  if(unit < Minutes) {
342  if(unit < Seconds) {
343  if(unit < Milliseconds) {
344  if(unit < Microseconds) {
345  divmod(Nanoseconds, 1000);
346  }
347  divmod(Microseconds, 1000);
348  }
349  divmod(Milliseconds, 1000);
350  }
351  divmod(Seconds, 60);
352  }
353  divmod(Minutes, 60);
354  }
355  divmod(Hours, 24);
356  }
357 
358  nanoseconds = elem[Nanoseconds];
359  microseconds = elem[Microseconds];
360  milliseconds = elem[Milliseconds];
361  seconds = elem[Seconds];
362  minutes = elem[Minutes];
363  hours = elem[Hours];
364  days = elem[Days];
365 }
366 
370 template <typename T> struct Time {
371  Time() = default;
372 
373  Time(Unit unit, T time) : unit(unit), time(time)
374  {
375  }
376 
377  void set(Unit unit, T time)
378  {
379  this->unit = unit;
380  this->time = time;
381  }
382 
383  operator T() const
384  {
385  return time;
386  }
387 
388  String toString() const
389  {
390  String s(time);
391  s += unitToString(unit);
392  return s;
393  }
394 
395  friend Time& operator+(Time lhs, const Time& rhs)
396  {
397  lhs.time += rhs.as(lhs.unit);
398  return lhs;
399  }
400 
402  {
403  time += rhs.as(unit);
404  return *this;
405  }
406 
407  TimeValue value() const
408  {
409  return TimeValue(unit, time);
410  }
411 
412  template <Unit unitTo> Time as() const
413  {
414  return Time(unitTo, convert(time, unit, unitTo));
415  }
416 
417  Time as(Unit unitTo) const
418  {
419  return Time(unitTo, convert(time, unit, unitTo));
420  }
421 
422  Unit unit = Seconds;
423  T time = 0;
424 };
425 
429 template <typename T> Time<T> time(Unit unit, T value)
430 {
431  return Time<T>(unit, value);
432 }
433 
437 template <typename Clock_, typename T> struct Ticks {
438  using Clock = Clock_;
439 
440  static constexpr Clock clock()
441  {
442  return Clock();
443  }
444 
445  Ticks(T ticks) : ticks(ticks)
446  {
447  }
448 
449  operator T() const
450  {
451  return ticks;
452  }
453 
454  String toString() const
455  {
456  return String(ticks);
457  }
458 
459  template <Unit unit> Time<T> as()
460  {
461  return Time<T>(unit, Clock::template ticksToTime<unit>(ticks));
462  }
463 
464  T ticks;
465 };
466 
474 template <class Clock_, Unit unit_, uint64_t time_> struct TimeConst {
475  using Clock = Clock_;
476  using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>;
477 
478  static constexpr Clock clock()
479  {
480  return Clock();
481  }
482 
483  static constexpr Unit unit()
484  {
485  return unit_;
486  }
487 
488  static constexpr uint64_t time()
489  {
490  return time_;
491  }
492 
493  constexpr operator uint64_t()
494  {
495  return time_;
496  }
497 
503  {
504  return BasicRatio32{TicksPerUnit::num, TicksPerUnit::den};
505  }
506 
511  static constexpr uint64_t ticks()
512  {
513  return round(double(time_) * TicksPerUnit::num / TicksPerUnit::den);
514  }
515 
520  static constexpr void check()
521  {
522  static_assert(ticks() <= Clock::maxTicks(), "Time exceeds clock range");
523  }
524 
529  static constexpr uint64_t clockTime()
530  {
531  return round(double(ticks()) * TicksPerUnit::den / TicksPerUnit::num);
532  }
533 
539  template <Unit unit> static constexpr uint64_t as()
540  {
541  return convert<time_, unit_, unit>();
542  }
543 
544  static TimeValue value()
545  {
546  return TimeValue(unit_, time_);
547  }
548 
550  {
551  return TimeValue(unit_, clockTime());
552  }
553 
554  static String toString()
555  {
556  return Time<uint64_t>(unit_, time_).toString();
557  }
558 };
559 
566 template <class Clock_, uint64_t ticks_> struct TicksConst {
567  using Clock = Clock_;
568  using TickType = uint64_t;
569  using TimeType = uint64_t;
570 
571  static constexpr TickType ticks()
572  {
573  return ticks_;
574  }
575 
576  constexpr operator TickType()
577  {
578  return ticks_;
579  }
580 
585  static constexpr void check()
586  {
587  static_assert(ticks_ <= Clock::maxTicks(), "Ticks exceeds clock range");
588  }
589 
590  template <Unit unit>
591  using TimeConst = TimeConst<Clock, unit,
592  TimeType(round(double(ticks_) * Clock::template TicksPerUnit<unit>::den /
593  Clock::template TicksPerUnit<unit>::num))>;
594 
600  template <Unit unit> static constexpr TimeConst<unit> as()
601  {
602  return TimeConst<unit>();
603  }
604 
605  static String toString()
606  {
607  return String(ticks_);
608  }
609 };
610 
619 template <Unit unitsFrom, Unit unitsTo, typename TimeType> __forceinline TimeType convert(const TimeType& time)
620 {
621  if(unitsFrom == unitsTo) {
622  return time;
623  }
624 
625  using R = std::ratio_divide<UnitTickRatio<unitsTo>, UnitTickRatio<unitsFrom>>;
626  return muldiv<R::num, R::den>(time);
627 }
628 
636 template <class Clock_, Unit unit_, typename TimeType_> struct TimeSource : public Clock_ {
637  using Clock = Clock_;
638  using TimeType = TimeType_;
639  using TicksPerUnit = typename Clock::template TicksPerUnit<unit_>;
640  template <uint64_t time> using TimeConst = TimeConst<Clock, unit_, time>;
641  template <uint64_t ticks> using TicksConst = TicksConst<Clock, ticks>;
642 
643  static constexpr Unit unit()
644  {
645  return unit_;
646  }
647 
652  static constexpr BasicRatio32 ticksPerUnit()
653  {
654  return {TicksPerUnit::num, TicksPerUnit::den};
655  }
656 
660  using MaxClockTime = typename Clock::template MaxTime<unit_>;
661  static constexpr MaxClockTime maxClockTime()
662  {
663  return MaxClockTime();
664  }
665 
666  // Check against arbitrary minimum - could be 1, but is that actually useful?
667  static_assert(maxClockTime() >= 5, "Time units too large for Clock ");
668 
675  template <uint64_t time> static constexpr TimeConst<time> timeConst()
676  {
677  return TimeConst<time>();
678  }
679 
686  template <uint64_t ticks> static constexpr TicksConst<ticks> ticksConst()
687  {
688  return TicksConst<ticks>();
689  }
690 
695  static constexpr Time<TimeType_> maxCalcTime()
696  {
698  }
699 
705  {
707  }
708 
715  {
716  return muldiv<TicksPerUnit::num, TicksPerUnit::den>(time);
717  }
718 
724  template <uint64_t time> static constexpr uint64_t timeToTicks()
725  {
726  return TimeConst<time>::ticks();
727  }
728 
734  template <uint64_t ticks> static constexpr uint64_t ticksToTime()
735  {
736  return TicksConst<ticks>::template as<unit>();
737  }
738 
745  {
746  return Time<TimeType_>(unit_, muldiv<TicksPerUnit::den, TicksPerUnit::num>(ticks));
747  }
748 
749  static String toString()
750  {
751  String s;
752  s += Clock::toString();
753  s += '/';
754  s += sizeof(TimeType) * 8;
755  s += "-bit/";
756  s += unitToLongString(unit_);
757  return s;
758  }
759 };
760 
761 } // namespace NanoTime
762 
typename MaxTicks::template TimeConst< unit > MaxTime
Definition: NanoTime.h:161
static Ticks< TimeType > timeToTicks(TimeType time)
Get the number of ticks for a given time.
Definition: NanoTime.h:212
static constexpr BasicRatio32 ticksPerUnit()
Number of clock ticks per unit of time.
Definition: NanoTime.h:652
static constexpr uint64_t as()
Obtain the time in a different set of units.
Definition: NanoTime.h:539
#define round(x)
Definition: ArduinoCompat.h:23
static Ratio32 ticksPerUnit()
Get ticks per unit as a Ratio object.
Definition: NanoTime.h:502
static String toString()
Definition: NanoTime.h:605
typename Clock::template TicksPerUnit< unit_ > TicksPerUnit
Definition: NanoTime.h:639
Unit
Identify units for a scalar quantity of time.
Definition: NanoTime.h:45
#define __forceinline
Definition: sming_attr.h:13
typename Clock::template TicksPerUnit< unit_ > TicksPerUnit
Definition: NanoTime.h:476
Definition: NanoTime.h:50
Class to simplify calculations of finite rationals at runtime.
Definition: Rational.h:65
Class template representing a physical Clock with fixed timing characteristics.
Definition: NanoTime.h:137
Time as(Unit unitTo) const
Definition: NanoTime.h:417
static constexpr TicksConst< ticks > ticksConst()
Class template defining a fixed tick quantity.
Definition: NanoTime.h:192
static constexpr uint64_t ticks()
Return the corresponding tick value for the time interval.
Definition: NanoTime.h:511
static constexpr uint64_t num
Definition: NanoTime.h:115
Class template to define tick std::ratio type.
Definition: NanoTime.h:114
Definition: NanoTime.h:53
String toString() const
Definition: NanoTime.h:388
static constexpr TickType ticks()
Definition: NanoTime.h:571
friend Time & operator+(Time lhs, const Time &rhs)
Definition: NanoTime.h:395
Definition: NanoTime.h:48
static constexpr void check()
Use this function to perform a static (compile-time) range check against Clock maxTicks.
Definition: NanoTime.h:520
static constexpr TimeConst< unit > as()
Get the time for the tick count in a specific time unit.
Definition: NanoTime.h:600
Time< T > as()
Definition: NanoTime.h:459
const char * unitToString(Unit unit)
Get a string identifying the given time units, e.g. "ns".
static constexpr MaxTicks maxTicks()
Definition: NanoTime.h:156
The String class.
Definition: WString.h:136
uint32_t getNanoseconds() const
Get sub-second time entirely in nanoseconds.
Definition: NanoTime.h:303
Time as() const
Definition: NanoTime.h:412
Class template to represent a fixed time value for a specific Clock.
Definition: NanoTime.h:120
Ticks(T ticks)
Definition: NanoTime.h:445
static constexpr ValType maxValue()
Get the maximum value which can be used for a muldiv calcuation without overflowing.
Definition: muldiv.h:75
String toString() const
Definition: NanoTime.h:454
Class template for accessing a Clock in specific time units.
Definition: NanoTime.h:122
void set(Unit unit, TimeType time)
Definition: NanoTime.h:326
uint64_t TickType
Definition: NanoTime.h:568
uint32_t getMicroseconds() const
Get sub-second time entirely in microseconds.
Definition: NanoTime.h:295
Time< T > time(Unit unit, T value)
Helper function to create a Time and deduce the type.
Definition: NanoTime.h:429
T ticks
Definition: NanoTime.h:464
String toString() const
Get frequency as compact string.
NanoTime::Nanoseconds Clock
Definition: NanoTime.h:637
Definition: NanoTime.h:49
constexpr BasicRatio32 unitTicks[UnitMax+1]
List of clock ticks for each supported unit of time.
Definition: NanoTime.h:96
static constexpr Clock clock()
Definition: NanoTime.h:440
static String toString()
Definition: NanoTime.h:227
static TimeValue value()
Definition: NanoTime.h:544
static constexpr TicksConst< ticks > ticksConst()
Class template defining a fixed tick quantity.
Definition: NanoTime.h:686
T time
Definition: NanoTime.h:423
static constexpr Time< TimeType_ > maxCalcTime()
The maximum time value supported by timeToTicks without overflowing.
Definition: NanoTime.h:695
static constexpr Clock clock()
Definition: NanoTime.h:478
static constexpr uint64_t ticksToTime()
Get the time for a given number of clock ticks.
Definition: NanoTime.h:734
TimeConst< Clock, unit_, time > TimeConst
Definition: NanoTime.h:640
TimeValue(Unit unit, TimeType time)
Resolve a time value into constituent components.
Definition: NanoTime.h:285
const char * unitToLongString(Unit unit)
Get a long string identifying the given time units, e.g. "seconds".
A time time broken into its constituent elements.
Definition: NanoTime.h:277
static constexpr MaxClockTime maxClockTime()
Definition: NanoTime.h:661
Definition: NanoTime.h:52
static constexpr uint64_t timeToTicks()
Get the number of ticks for a given time.
Definition: NanoTime.h:724
static constexpr TimeSource< unit, TimeType > timeSource()
Create a Time Source for this Clock.
Definition: NanoTime.h:202
static constexpr uint64_t time()
Definition: NanoTime.h:488
Time & operator+=(Time< T > rhs)
Definition: NanoTime.h:401
static constexpr TimeConst< time > timeConst()
Obtain a TimeConst type representing the given time quantity.
Definition: NanoTime.h:675
TicksConst< Clock, ticks > TicksConst
Definition: NanoTime.h:641
static constexpr uint32_t frequency()
Definition: NanoTime.h:150
static constexpr TimeConst< unit, time > timeConst()
Class template defining a fixed time quantity.
Definition: NanoTime.h:182
static Ratio32 ticksPerUnit(Unit unit)
Get ticks per unit as a Ratio object.
Definition: NanoTime.h:171
constexpr uint64_t convert()
Function template to convert a constant time quantity from one unit to another.
Definition: NanoTime.h:249
Class template representing a fixed clock tick count.
Definition: NanoTime.h:121
Ratio< uint32_t > Ratio32
Definition: Rational.h:157
TimeValue value() const
Definition: NanoTime.h:407
static constexpr MaxTime< unit > maxTime()
Definition: NanoTime.h:162
uint64_t TimeType
Definition: NanoTime.h:569
Class to handle a tick value associated with a clock.
Definition: NanoTime.h:124
Time(Unit unit, T time)
Definition: NanoTime.h:373
Class to handle a simple time value with associated unit.
Definition: NanoTime.h:123
static TimeValue clockValue()
Definition: NanoTime.h:549
TimeSource< Clock, unit, TimeType > TimeSource
Definition: NanoTime.h:142
static constexpr const char * typeName()
Definition: NanoTime.h:145
static Ticks< Clock_, TimeType_ > timeToTicks(TimeType time)
Get the number of ticks for a given time.
Definition: NanoTime.h:714
std::ratio_divide< std::ratio< frequency_ >, UnitTickRatio< unit > > TicksPerUnit
Definition: NanoTime.h:141
Definition: NanoTime.h:46
uint32_t frequency
Definition: NanoTime.h:90
typename Clock::template MaxTime< unit_ > MaxClockTime
Get the time corresponding to the maximum clock tick value.
Definition: NanoTime.h:660
Definition: NanoTime.h:47
Class to represent a frequency.
Definition: NanoTime.h:69
static Time< TimeType_ > ticksToTime(TimeType ticks)
Get the time for a given number of clock ticks.
Definition: NanoTime.h:744
static constexpr Unit unit()
Definition: NanoTime.h:483
static String toString()
Definition: NanoTime.h:554
static constexpr uint64_t clockTime()
Obtain the actual Clock time by converting tick value.
Definition: NanoTime.h:529
Unit unit
Definition: NanoTime.h:422
static String toString()
Definition: NanoTime.h:749
Definition: NanoTime.h:39
A basic rational fraction, constexpr-compatible.
Definition: Rational.h:37
static Time< TimeType > ticksToTime(TimeType ticks)
Get the time for a given number of clock ticks.
Definition: NanoTime.h:222
Frequency(uint32_t frequency)
Definition: NanoTime.h:70
Definition: NanoTime.h:51
static constexpr void check()
Obtain the tick count with a static range check against Clock maxTicks.
Definition: NanoTime.h:585
TimeConst< Clock, unit, TimeType(round(double(ticks_) *Clock::template TicksPerUnit< unit >::den/Clock::template TicksPerUnit< unit >::num))> TimeConst
Definition: NanoTime.h:593
static constexpr Unit unit()
Definition: NanoTime.h:643
static constexpr Ticks< Clock_, TimeType_ > maxCalcTicks()
The maximum tick value supported by ticksToTime without overflowing.
Definition: NanoTime.h:704