util: use time::nanoseconds in RttEstimator

Refs: #4887
Change-Id: Ie6469732fec53cca4b274b36d7ec58eff7addc1b
diff --git a/ndn-cxx/util/rtt-estimator.cpp b/ndn-cxx/util/rtt-estimator.cpp
index c0d62d6..e214ebb 100644
--- a/ndn-cxx/util/rtt-estimator.cpp
+++ b/ndn-cxx/util/rtt-estimator.cpp
@@ -30,18 +30,25 @@
 
 RttEstimator::RttEstimator(const Options& options)
   : m_options(options)
-  , m_sRtt(std::numeric_limits<double>::quiet_NaN())
-  , m_rttVar(std::numeric_limits<double>::quiet_NaN())
+  , m_sRtt(0)
+  , m_rttVar(0)
   , m_rto(m_options.initialRto)
-  , m_rttMin(std::numeric_limits<double>::max())
-  , m_rttMax(std::numeric_limits<double>::min())
-  , m_rttAvg(0.0)
+  , m_rttMin(time::nanoseconds::max())
+  , m_rttMax(time::nanoseconds::min())
+  , m_rttAvg(0)
   , m_nRttSamples(0)
 {
+  BOOST_ASSERT(m_options.alpha >= 0 && m_options.alpha <= 1);
+  BOOST_ASSERT(m_options.beta >= 0 && m_options.beta <= 1);
+  BOOST_ASSERT(m_options.initialRto >= 0_ns);
+  BOOST_ASSERT(m_options.minRto >= 0_ns);
+  BOOST_ASSERT(m_options.maxRto >= m_options.minRto);
+  BOOST_ASSERT(m_options.k >= 0);
+  BOOST_ASSERT(m_options.rtoBackoffMultiplier >= 1);
 }
 
 void
-RttEstimator::addMeasurement(MillisecondsDouble rtt, size_t nExpectedSamples,
+RttEstimator::addMeasurement(time::nanoseconds rtt, size_t nExpectedSamples,
                              optional<uint64_t> segNum)
 {
   BOOST_ASSERT(nExpectedSamples > 0);
@@ -49,17 +56,17 @@
   if (m_nRttSamples == 0) { // first measurement
     m_sRtt = rtt;
     m_rttVar = m_sRtt / 2;
-    m_rto = m_sRtt + m_options.k * m_rttVar;
   }
   else {
     double alpha = m_options.alpha / nExpectedSamples;
     double beta = m_options.beta / nExpectedSamples;
-    m_rttVar = (1 - beta) * m_rttVar + beta * time::abs(m_sRtt - rtt);
-    m_sRtt = (1 - alpha) * m_sRtt + alpha * rtt;
-    m_rto = m_sRtt + m_options.k * m_rttVar;
+    m_rttVar = time::duration_cast<time::nanoseconds>((1 - beta) * m_rttVar +
+                                                      beta * time::abs(m_sRtt - rtt));
+    m_sRtt = time::duration_cast<time::nanoseconds>((1 - alpha) * m_sRtt + alpha * rtt);
   }
+  m_rto = clamp(m_sRtt + m_options.k * m_rttVar,
+                m_options.minRto, m_options.maxRto);
 
-  m_rto = clamp(m_rto, m_options.minRto, m_options.maxRto);
   afterMeasurement({rtt, m_sRtt, m_rttVar, m_rto, segNum});
 
   m_rttAvg = (m_nRttSamples * m_rttAvg + rtt) / (m_nRttSamples + 1);
diff --git a/ndn-cxx/util/rtt-estimator.hpp b/ndn-cxx/util/rtt-estimator.hpp
index bcd2c12..da43ea4 100644
--- a/ndn-cxx/util/rtt-estimator.hpp
+++ b/ndn-cxx/util/rtt-estimator.hpp
@@ -41,8 +41,6 @@
 class RttEstimator
 {
 public:
-  using MillisecondsDouble = time::duration<double, time::milliseconds::period>;
-
   class Options
   {
   public:
@@ -54,9 +52,9 @@
   public:
     double alpha = 0.125; ///< weight of exponential moving average for smoothed RTT
     double beta = 0.25; ///< weight of exponential moving average for RTT variation
-    MillisecondsDouble initialRto{1000.0}; ///< initial RTO value
-    MillisecondsDouble minRto{200.0}; ///< lower bound of RTO
-    MillisecondsDouble maxRto{60000.0}; ///< upper bound of RTO
+    time::nanoseconds initialRto = 1_s; ///< initial RTO value
+    time::nanoseconds minRto = 200_ms; ///< lower bound of RTO
+    time::nanoseconds maxRto = 1_min; ///< upper bound of RTO
     int k = 4; ///< RTT variation multiplier used when calculating RTO
     int rtoBackoffMultiplier = 2; ///< RTO multiplier used in backoff operation
   };
@@ -83,13 +81,13 @@
    * @note Do not call this function with RTT samples from retransmitted Interests (per Karn's algorithm).
    */
   void
-  addMeasurement(MillisecondsDouble rtt, size_t nExpectedSamples,
+  addMeasurement(time::nanoseconds rtt, size_t nExpectedSamples,
                  optional<uint64_t> segNum = nullopt);
 
   /**
    * @brief Returns the estimated RTO value.
    */
-  MillisecondsDouble
+  time::nanoseconds
   getEstimatedRto() const
   {
     return m_rto;
@@ -98,7 +96,7 @@
   /**
    * @brief Returns the minimum RTT observed.
    */
-  MillisecondsDouble
+  time::nanoseconds
   getMinRtt() const
   {
     return m_rttMin;
@@ -107,7 +105,7 @@
   /**
    * @brief Returns the maximum RTT observed.
    */
-  MillisecondsDouble
+  time::nanoseconds
   getMaxRtt() const
   {
     return m_rttMax;
@@ -116,22 +114,31 @@
   /**
    * @brief Returns the average RTT.
    */
-  MillisecondsDouble
+  time::nanoseconds
   getAvgRtt() const
   {
     return m_rttAvg;
   }
 
   /**
-   * @brief Returns the smoothed RTT.
+   * @brief Returns the smoothed RTT value (SRTT).
    */
-  MillisecondsDouble
+  time::nanoseconds
   getSmoothedRtt() const
   {
     return m_sRtt;
   }
 
   /**
+   * @brief Returns the RTT variation (RTTVAR).
+   */
+  time::nanoseconds
+  getRttVariation() const
+  {
+    return m_rttVar;
+  }
+
+  /**
    * @brief Backoff RTO by a factor of Options::rtoBackoffMultiplier.
    */
   void
@@ -140,10 +147,10 @@
 public:
   struct Sample
   {
-    MillisecondsDouble rtt;     ///< measured RTT
-    MillisecondsDouble sRtt;    ///< smoothed RTT
-    MillisecondsDouble rttVar;  ///< RTT variation
-    MillisecondsDouble rto;     ///< retransmission timeout
+    time::nanoseconds rtt;      ///< measured RTT
+    time::nanoseconds sRtt;     ///< smoothed RTT
+    time::nanoseconds rttVar;   ///< RTT variation
+    time::nanoseconds rto;      ///< retransmission timeout
     optional<uint64_t> segNum;  ///< segment number, see description in addMeasurement()
   };
 
@@ -151,16 +158,12 @@
 
 private:
   const Options m_options;
-
-NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PRIVATE:
-  MillisecondsDouble m_sRtt;    ///< smoothed round-trip time
-  MillisecondsDouble m_rttVar;  ///< round-trip time variation
-  MillisecondsDouble m_rto;     ///< retransmission timeout
-
-private:
-  MillisecondsDouble m_rttMin;
-  MillisecondsDouble m_rttMax;
-  MillisecondsDouble m_rttAvg;
+  time::nanoseconds m_sRtt;   ///< smoothed round-trip time
+  time::nanoseconds m_rttVar; ///< round-trip time variation
+  time::nanoseconds m_rto;    ///< retransmission timeout
+  time::nanoseconds m_rttMin;
+  time::nanoseconds m_rttMax;
+  time::nanoseconds m_rttAvg;
   int64_t m_nRttSamples;
 };
 
diff --git a/tests/unit/util/rtt-estimator.t.cpp b/tests/unit/util/rtt-estimator.t.cpp
index 59041e4..10c2c74 100644
--- a/tests/unit/util/rtt-estimator.t.cpp
+++ b/tests/unit/util/rtt-estimator.t.cpp
@@ -34,117 +34,110 @@
 BOOST_AUTO_TEST_SUITE(Util)
 BOOST_AUTO_TEST_SUITE(TestRttEstimator)
 
-using Millis = RttEstimator::MillisecondsDouble;
-
 BOOST_AUTO_TEST_CASE(MinAvgMaxRtt)
 {
   RttEstimator rttEstimator;
 
   // check initial values
-  BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), std::numeric_limits<double>::max(), 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 0.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), std::numeric_limits<double>::min(), 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getMinRtt().count(), std::numeric_limits<time::nanoseconds::rep>::max());
+  BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt().count(), 0);
+  BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt().count(), std::numeric_limits<time::nanoseconds::rep>::min());
 
   // start with three samples
-  rttEstimator.addMeasurement(Millis(100), 1);
-  rttEstimator.addMeasurement(Millis(400), 1);
-  rttEstimator.addMeasurement(Millis(250), 1);
+  rttEstimator.addMeasurement(100_ms, 1);
+  rttEstimator.addMeasurement(400_ms, 1);
+  rttEstimator.addMeasurement(250_ms, 1);
 
-  BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), 100.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 250.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), 400.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getMinRtt(), 100_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt(), 250_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt(), 400_ms);
 
   // add another sample (new minimum)
-  rttEstimator.addMeasurement(Millis(50), 2);
-  BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), 50.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 200.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), 400.0, 0.001);
+  rttEstimator.addMeasurement(50_ms, 2);
+  BOOST_CHECK_EQUAL(rttEstimator.getMinRtt(), 50_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt(), 200_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt(), 400_ms);
 
   // add another sample (new maximum)
-  rttEstimator.addMeasurement(Millis(700), 1);
-  BOOST_CHECK_CLOSE(rttEstimator.getMinRtt().count(), 50.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getAvgRtt().count(), 300.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getMaxRtt().count(), 700.0, 0.001);
+  rttEstimator.addMeasurement(700_ms, 1);
+  BOOST_CHECK_EQUAL(rttEstimator.getMinRtt(), 50_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getAvgRtt(), 300_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getMaxRtt(), 700_ms);
 }
 
 BOOST_AUTO_TEST_CASE(EstimatedRto)
 {
   RttEstimator::Options opts;
-  opts.initialRto = Millis(1000);
-  opts.maxRto = Millis(4000);
+  opts.initialRto = 400_ms;
+  opts.maxRto = 2_s;
   RttEstimator rttEstimator(opts);
 
   // check initial values
-  BOOST_CHECK(std::isnan(rttEstimator.m_sRtt.count()));
-  BOOST_CHECK(std::isnan(rttEstimator.m_rttVar.count()));
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1000.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 0_ns);
+  BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 0_ns);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), opts.initialRto);
 
   // first measurement
-  rttEstimator.addMeasurement(Millis(100), 1);
+  rttEstimator.addMeasurement(200_ms, 1);
 
-  BOOST_CHECK_CLOSE(rttEstimator.m_sRtt.count(), 100.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 50.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 300.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 200_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 100_ms);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 600_ms);
 
-  rttEstimator.m_sRtt = Millis(500);
-  rttEstimator.m_rttVar = Millis(100);
-  rttEstimator.m_rto = Millis(900);
+  rttEstimator.addMeasurement(100_ms, 1);
 
-  rttEstimator.addMeasurement(Millis(100), 1);
-
-  BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 450.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 175.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1150.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 187500_us);
+  BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 100000_us);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 587500_us);
 
   // expected samples larger than 1
-  rttEstimator.addMeasurement(Millis(100), 5);
+  rttEstimator.addMeasurement(50_ms, 5);
 
-  BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 441.25, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 183.75, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1176.25, 0.001);
-
-  rttEstimator.m_sRtt = Millis(100.0);
-  rttEstimator.m_rttVar = Millis(30.0);
-  rttEstimator.m_rto = Millis(220.0);
+  BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 184062500_ns);
+  BOOST_CHECK_EQUAL(rttEstimator.getRttVariation(), 101875000_ns);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 591562500_ns);
 
   // check if minRto works
-  rttEstimator.addMeasurement(Millis(100), 1);
+  for (int i = 0; i < 20; i++) {
+    rttEstimator.addMeasurement(10_ms, 1);
+  }
 
-  BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 100.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 22.5, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 200.0, 0.001);
-
-  rttEstimator.m_sRtt = Millis(2000);
-  rttEstimator.m_rttVar = Millis(400);
-  rttEstimator.m_rto = Millis(3600);
+  BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 22046646_ns);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), opts.minRto);
 
   // check if maxRto works
-  rttEstimator.addMeasurement(Millis(100), 1);
+  for (int i = 0; i < 10; i++) {
+    rttEstimator.addMeasurement(1_s, 1);
+    rttEstimator.addMeasurement(10_ms, 1);
+  }
 
-  BOOST_CHECK_CLOSE(rttEstimator.getSmoothedRtt().count(), 1762.5, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.m_rttVar.count(), 775.0, 0.001);
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 4000.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getSmoothedRtt(), 440859284_ns);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), opts.maxRto);
 }
 
 BOOST_AUTO_TEST_CASE(BackoffRto)
 {
   RttEstimator::Options opts;
-  opts.initialRto = Millis(500);
-  opts.maxRto = Millis(4000);
+  opts.initialRto = 500_ms;
+  opts.maxRto = 4_s;
   RttEstimator rttEstimator(opts);
 
   rttEstimator.backoffRto();
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 1000.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 1_s);
 
   // check if minRto works
-  rttEstimator.m_rto = Millis(10);
+  for (int i = 0; i < 10; i++) {
+    rttEstimator.addMeasurement(5_ms, 1);
+  }
   rttEstimator.backoffRto();
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 200.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 400_ms);
 
   // check if maxRto works
-  rttEstimator.m_rto = Millis(3000);
+  for (int i = 0; i < 10; i++) {
+    rttEstimator.addMeasurement(5_s, 1);
+  }
   rttEstimator.backoffRto();
-  BOOST_CHECK_CLOSE(rttEstimator.getEstimatedRto().count(), 4000.0, 0.001);
+  BOOST_CHECK_EQUAL(rttEstimator.getEstimatedRto(), 4_s);
 }
 
 BOOST_AUTO_TEST_CASE(AfterMeasurement)
@@ -154,24 +147,24 @@
   int nHandlerInvocations = 0;
   rttEstimator.afterMeasurement.connectSingleShot([&nHandlerInvocations] (const auto& sample) {
     ++nHandlerInvocations;
-    BOOST_CHECK_CLOSE(sample.rtt.count(), 80.0, 0.001);
-    BOOST_CHECK_CLOSE(sample.sRtt.count(), 80.0, 0.001);
-    BOOST_CHECK_CLOSE(sample.rttVar.count(), 40.0, 0.001);
-    BOOST_CHECK_CLOSE(sample.rto.count(), 240.0, 0.001);
+    BOOST_CHECK_EQUAL(sample.rtt, 80_ms);
+    BOOST_CHECK_EQUAL(sample.sRtt, 80_ms);
+    BOOST_CHECK_EQUAL(sample.rttVar, 40_ms);
+    BOOST_CHECK_EQUAL(sample.rto, 240_ms);
     BOOST_CHECK(!sample.segNum.has_value());
   });
-  rttEstimator.addMeasurement(Millis(80), 1);
+  rttEstimator.addMeasurement(80_ms, 1);
   BOOST_CHECK_EQUAL(nHandlerInvocations, 1);
 
   rttEstimator.afterMeasurement.connectSingleShot([&nHandlerInvocations] (const auto& sample) {
     ++nHandlerInvocations;
-    BOOST_CHECK_CLOSE(sample.rtt.count(), 40.0, 0.001);
-    BOOST_CHECK_CLOSE(sample.sRtt.count(), 75.0, 0.001);
-    BOOST_CHECK_CLOSE(sample.rttVar.count(), 40.0, 0.001);
-    BOOST_CHECK_CLOSE(sample.rto.count(), 235.0, 0.001);
+    BOOST_CHECK_EQUAL(sample.rtt, 40_ms);
+    BOOST_CHECK_EQUAL(sample.sRtt, 75_ms);
+    BOOST_CHECK_EQUAL(sample.rttVar, 40_ms);
+    BOOST_CHECK_EQUAL(sample.rto, 235_ms);
     BOOST_CHECK(sample.segNum == 42U);
   });
-  rttEstimator.addMeasurement(Millis(40), 1, 42);
+  rttEstimator.addMeasurement(40_ms, 1, 42);
   BOOST_CHECK_EQUAL(nHandlerInvocations, 2);
 }