summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Blum2013-06-21 19:53:34 -0700
committerJosh Blum2013-06-21 19:53:34 -0700
commit78139a05aa2f1516688f29359538fbc09b8c3e2e (patch)
tree5e18889f2fc3cbcb22d722ac70176d5809f0ea27
parentb6e5debf3d0b9fddd5f274bf16bd3d5e09219639 (diff)
parent90579c89748704dc1d7e9d80120c42988481197c (diff)
downloadsandhi-78139a05aa2f1516688f29359538fbc09b8c3e2e.tar.gz
sandhi-78139a05aa2f1516688f29359538fbc09b8c3e2e.tar.bz2
sandhi-78139a05aa2f1516688f29359538fbc09b8c3e2e.zip
Merge branch 'time_tag_work'
-rw-r--r--include/gras/CMakeLists.txt2
-rw-r--r--include/gras/time_tag.hpp65
-rw-r--r--include/gras/time_tag.i49
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/serialize_types.cpp15
-rw-r--r--lib/time_tag.cpp89
-rw-r--r--python/gras/CMakeLists.txt2
-rw-r--r--python/gras/GRAS_TimeTag.i14
-rw-r--r--python/gras/__init__.py1
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/serialize_tags_test.cpp9
-rw-r--r--tests/time_tags_test.py74
12 files changed, 322 insertions, 0 deletions
diff --git a/include/gras/CMakeLists.txt b/include/gras/CMakeLists.txt
index d0e3cfa..176d5e5 100644
--- a/include/gras/CMakeLists.txt
+++ b/include/gras/CMakeLists.txt
@@ -17,6 +17,8 @@ install(FILES
sbuffer.i
tags.hpp
tags.i
+ time_tag.hpp
+ time_tag.i
tag_iter.hpp
tag_iter.i
thread_pool.hpp
diff --git a/include/gras/time_tag.hpp b/include/gras/time_tag.hpp
new file mode 100644
index 0000000..9459f9f
--- /dev/null
+++ b/include/gras/time_tag.hpp
@@ -0,0 +1,65 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+#ifndef INCLUDED_GRAS_TIME_TAG_HPP
+#define INCLUDED_GRAS_TIME_TAG_HPP
+
+#include <gras/gras.hpp>
+#include <gras/chrono.hpp>
+#include <PMC/PMC.hpp>
+#include <boost/operators.hpp>
+
+namespace gras
+{
+
+/*!
+ * TimeTag represents an absolute time or a time delta.
+ * A TimeTag can be converted to and from a tick count.
+ * Conversion support is provided for the pseudo-standard
+ * PMCTuple format often used inside a StreamTag value.
+ * And TimeTag supports overloaded arithmetic operations.
+ */
+struct GRAS_API TimeTag :
+ boost::less_than_comparable<TimeTag>,
+ boost::additive<TimeTag>
+{
+ //! Default contructor - hold time 0
+ TimeTag(void);
+
+ //! Create a time tag from ticks w/ default ticks per second
+ static TimeTag from_ticks(const time_ticks_t ticks);
+
+ //! Create a time tag from ticks w/ specified ticks per second
+ static TimeTag from_ticks(const time_ticks_t ticks, const double rate);
+
+ //! Create a time tag from a PMC containing a PMCTuple<2>(uint64, double)
+ static TimeTag from_pmc(const PMCC &p);
+
+ //! Convert this time tag to ticks w/ default ticks per second
+ time_ticks_t to_ticks(void);
+
+ //! Convert this time tag to ticks w/ specified ticks per second
+ time_ticks_t to_ticks(const double rate);
+
+ //! Convert this time tag to a PMC containing a PMCTuple<2>(uint64, double)
+ PMCC to_pmc(void);
+
+ //! Addition for additive interface
+ TimeTag &operator+=(const TimeTag &);
+
+ //! Subtraction for additive interface
+ TimeTag &operator-=(const TimeTag &);
+
+ //! full seconds
+ time_ticks_t _fsecs;
+
+ //! fractional ticks
+ time_ticks_t _ticks;
+};
+
+GRAS_API bool operator<(const TimeTag &lhs, const TimeTag &rhs);
+
+GRAS_API bool operator==(const TimeTag &lhs, const TimeTag &rhs);
+
+} //namespace gras
+
+#endif /*INCLUDED_GRAS_TIME_TAG_HPP*/
diff --git a/include/gras/time_tag.i b/include/gras/time_tag.i
new file mode 100644
index 0000000..42deed7
--- /dev/null
+++ b/include/gras/time_tag.i
@@ -0,0 +1,49 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+#ifndef INCLUDED_GRAS_TIME_TAG_I
+#define INCLUDED_GRAS_TIME_TAG_I
+
+%{
+#include <gras/time_tag.hpp>
+%}
+
+////////////////////////////////////////////////////////////////////////
+// remove base class warning -- boost::less_than_comparable<TimeTag>
+// remove base class warning -- boost::additive<TimeTag>
+////////////////////////////////////////////////////////////////////////
+#pragma SWIG nowarn=401
+
+%include <gras/gras.hpp>
+%include <gras/chrono.hpp>
+%include <gras/time_tag.hpp>
+%import <PMC/PMC.i>
+
+////////////////////////////////////////////////////////////////////////
+// Make it pythonic
+////////////////////////////////////////////////////////////////////////
+%extend gras::TimeTag
+{
+ bool __nonzero__(void)
+ {
+ return ($self)->to_ticks() != 0;
+ }
+
+ int __cmp__(const TimeTag &other)
+ {
+ if ((*($self)) < other) return -1;
+ if ((*($self)) > other) return +1;
+ return 0;
+ }
+
+ TimeTag __add__(const TimeTag &other)
+ {
+ return (*($self)) + other;
+ }
+
+ TimeTag __sub__(const TimeTag &other)
+ {
+ return (*($self)) - other;
+ }
+}
+
+#endif /*INCLUDED_GRAS_TIME_TAG_I*/
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 44b3012..ab5d886 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -48,6 +48,7 @@ list(APPEND GRAS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/buffer_queue_circ.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer_queue_pool.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tags.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/time_tag.cpp
${CMAKE_CURRENT_SOURCE_DIR}/block.cpp
${CMAKE_CURRENT_SOURCE_DIR}/block_config.cpp
${CMAKE_CURRENT_SOURCE_DIR}/block_message.cpp
diff --git a/lib/serialize_types.cpp b/lib/serialize_types.cpp
index afa9481..fde7277 100644
--- a/lib/serialize_types.cpp
+++ b/lib/serialize_types.cpp
@@ -2,6 +2,7 @@
#include <gras/sbuffer.hpp>
#include <gras/tags.hpp>
+#include <gras/time_tag.hpp>
#include <PMC/Serialize.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/string.hpp>
@@ -82,3 +83,17 @@ void serialize(Archive &ar, gras::StreamTag &t, const unsigned int)
}}
PMC_SERIALIZE_EXPORT(gras::StreamTag, "PMC<gras::StreamTag>")
+
+/***********************************************************************
+ * support for time tag type
+ **********************************************************************/
+namespace boost { namespace serialization {
+template <class Archive>
+void serialize(Archive &ar, gras::TimeTag &t, const unsigned int)
+{
+ ar & t._fsecs;
+ ar & t._ticks;
+}
+}}
+
+PMC_SERIALIZE_EXPORT(gras::TimeTag, "PMC<gras::TimeTag>")
diff --git a/lib/time_tag.cpp b/lib/time_tag.cpp
new file mode 100644
index 0000000..d438fc3
--- /dev/null
+++ b/lib/time_tag.cpp
@@ -0,0 +1,89 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+#include <gras/time_tag.hpp>
+#include <PMC/Containers.hpp>
+#include <boost/cstdint.hpp> //uint64
+#include <boost/math/special_functions/round.hpp>
+
+using namespace gras;
+
+static TimeTag &normalize(TimeTag &t)
+{
+ int num = int(t._ticks/time_tps());
+ if (num < 0) num--; //stops negative ticks
+ t._fsecs += num;
+ t._ticks -= num*time_tps();
+ return t;
+}
+
+TimeTag::TimeTag(void):
+ _fsecs(0), _ticks(0)
+{/*NOP*/}
+
+TimeTag TimeTag::from_ticks(const time_ticks_t ticks)
+{
+ TimeTag t;
+ t._ticks = ticks;
+ return normalize(t);
+}
+
+TimeTag TimeTag::from_ticks(const time_ticks_t ticks, const double rate)
+{
+ TimeTag t;
+ t._fsecs = time_ticks_t(ticks/rate);
+ const double error = ticks - (t._fsecs*rate);
+ t._ticks = boost::math::llround((error*time_tps())/rate);
+ return normalize(t);
+}
+
+TimeTag TimeTag::from_pmc(const PMCC &p)
+{
+ TimeTag t;
+ const PMCTuple<2> &tuple = p.as<PMCTuple<2> >();
+ t._fsecs = tuple[0].as<boost::uint64_t>();
+ t._ticks = boost::math::llround(tuple[1].as<double>()*time_tps());
+ return normalize(t);
+}
+
+time_ticks_t TimeTag::to_ticks(void)
+{
+ return _fsecs*time_tps() + _ticks;
+}
+
+time_ticks_t TimeTag::to_ticks(const double rate)
+{
+ return _fsecs*time_tps() + boost::math::llround((_ticks*rate)/time_tps());
+}
+
+PMCC TimeTag::to_pmc(void)
+{
+ PMCTuple<2> tuple;
+ tuple[0] = PMC_M<boost::uint64_t>(_fsecs);
+ tuple[1] = PMC_M<double>(_ticks/double(time_tps()));
+ return PMC_M(tuple);
+}
+
+TimeTag &TimeTag::operator+=(const TimeTag &rhs)
+{
+ _fsecs += rhs._fsecs;
+ _ticks += rhs._ticks;
+ return normalize(*this);
+}
+
+TimeTag &TimeTag::operator-=(const TimeTag &rhs)
+{
+ _fsecs -= rhs._fsecs;
+ _ticks -= rhs._ticks;
+ return normalize(*this);
+}
+
+bool gras::operator<(const TimeTag &lhs, const TimeTag &rhs)
+{
+ if (lhs._fsecs == rhs._fsecs) return lhs._ticks < rhs._ticks;
+ return lhs._fsecs < rhs._fsecs;
+}
+
+bool gras::operator==(const TimeTag &lhs, const TimeTag &rhs)
+{
+ return (lhs._fsecs == rhs._fsecs) and (lhs._ticks == rhs._ticks);
+}
diff --git a/python/gras/CMakeLists.txt b/python/gras/CMakeLists.txt
index 8171e7f..a7fc8f3 100644
--- a/python/gras/CMakeLists.txt
+++ b/python/gras/CMakeLists.txt
@@ -19,6 +19,7 @@ set(GR_SWIG_LIBRARIES gras)
file(GLOB GR_SWIG_SOURCE_DEPS "${GRAS_SOURCE_DIR}/include/gras/*.i")
GR_SWIG_MAKE(GRAS_Tags GRAS_Tags.i)
+GR_SWIG_MAKE(GRAS_TimeTag GRAS_TimeTag.i)
GR_SWIG_MAKE(GRAS_Block GRAS_Block.i)
GR_SWIG_MAKE(GRAS_HierBlock GRAS_HierBlock.i)
GR_SWIG_MAKE(GRAS_ThreadPool GRAS_ThreadPool.i)
@@ -26,6 +27,7 @@ GR_SWIG_MAKE(GRAS_SBuffer GRAS_SBuffer.i)
GR_SWIG_INSTALL(
TARGETS
GRAS_Tags
+ GRAS_TimeTag
GRAS_Block
GRAS_HierBlock
GRAS_ThreadPool
diff --git a/python/gras/GRAS_TimeTag.i b/python/gras/GRAS_TimeTag.i
new file mode 100644
index 0000000..3b77dfb
--- /dev/null
+++ b/python/gras/GRAS_TimeTag.i
@@ -0,0 +1,14 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+
+%include <gras/time_tag.i>
+
+%include <PMC/Registry.i>
+
+DECL_PMC_SWIG_TYPE(gras::TimeTag, swig_time_tag)
+
+%pythoncode %{
+from GRAS_TimeTag import TimeTag
+%}
+
+REG_PMC_SWIG_TYPE(swig_time_tag, TimeTag)
diff --git a/python/gras/__init__.py b/python/gras/__init__.py
index 33d2b00..cf2c7f5 100644
--- a/python/gras/__init__.py
+++ b/python/gras/__init__.py
@@ -3,6 +3,7 @@
from PMC import *
from GRAS_SBuffer import SBufferConfig, SBuffer
from GRAS_Tags import Tag, StreamTag, PacketMsg
+from GRAS_TimeTag import TimeTag
from GRAS_Block import Block
from GRAS_HierBlock import HierBlock, TopBlock
from GRAS_ThreadPool import ThreadPoolConfig, ThreadPool
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 761239e..bc3815e 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -47,3 +47,4 @@ GR_ADD_TEST(thread_pool_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/th
GR_ADD_TEST(sbuffer_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/sbuffer_test.py)
GR_ADD_TEST(query_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/query_test.py)
GR_ADD_TEST(block_props_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/block_props_test.py)
+GR_ADD_TEST(time_tags_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/time_tags_test.py)
diff --git a/tests/serialize_tags_test.cpp b/tests/serialize_tags_test.cpp
index 58d9b1e..b4f9ec2 100644
--- a/tests/serialize_tags_test.cpp
+++ b/tests/serialize_tags_test.cpp
@@ -7,6 +7,7 @@
#include <PMC/PMC.hpp>
#include <PMC/Serialize.hpp>
#include <gras/tags.hpp>
+#include <gras/time_tag.hpp>
#include <cstdlib>
@@ -65,3 +66,11 @@ BOOST_AUTO_TEST_CASE(test_pkt_msg_type)
BOOST_CHECK(pkt_msg.info.eq(result_msg.info));
BOOST_CHECK(std::memcmp(pkt_msg.buff.get(), result_msg.buff.get(), pkt_msg.buff.length) == 0);
}
+
+BOOST_AUTO_TEST_CASE(test_time_tag_type)
+{
+ const gras::TimeTag t0 = gras::TimeTag::from_ticks(42);
+ PMCC result = loopback_test(PMC_M(t0));
+ const gras::TimeTag &t1 = result.as<gras::TimeTag>();
+ BOOST_CHECK(t0 == t1);
+}
diff --git a/tests/time_tags_test.py b/tests/time_tags_test.py
new file mode 100644
index 0000000..7d0221f
--- /dev/null
+++ b/tests/time_tags_test.py
@@ -0,0 +1,74 @@
+# Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+import unittest
+import gras
+import numpy
+import time
+
+class TimeTagsTest(unittest.TestCase):
+
+ def test_make_time_tag(self):
+ t0 = gras.TimeTag()
+
+ def test_make_time_tag_from_ticks(self):
+ t0 = gras.TimeTag.from_ticks(42)
+ self.assertEqual(t0.to_ticks(), 42)
+
+ t1 = gras.TimeTag.from_ticks(1000, 1e9)
+ self.assertEqual(t1.to_ticks(1e9), 1000)
+
+ t2 = gras.TimeTag.from_ticks(-1000, 1e9)
+ self.assertEqual(t2.to_ticks(1e9), -1000)
+
+ time_ns_now = long(time.time()*1e9)
+ t3 = gras.TimeTag.from_ticks(time_ns_now, 1e9)
+ self.assertEqual(t3.to_ticks(1e9), time_ns_now)
+
+ def test_time_tag_compare(self):
+ t0 = gras.TimeTag.from_ticks(42)
+ t1 = gras.TimeTag.from_ticks(-1000)
+ self.assertEqual(t0, t0)
+ self.assertEqual(t1, t1)
+ self.assertGreater(t0, t1)
+ self.assertLess(t1, t0)
+
+ def test_time_tag_add(self):
+ t0 = gras.TimeTag.from_ticks(42)
+ t1 = gras.TimeTag.from_ticks(-1000)
+ t2 = t0 + t1
+ self.assertEqual(t2.to_ticks(), (42 + -1000))
+
+ t3 = gras.TimeTag.from_ticks(99)
+ t4 = gras.TimeTag.from_ticks(-98)
+ t3 += t4
+ self.assertEqual(t3.to_ticks(), (99 + -98))
+
+ def test_time_tag_sub(self):
+ t0 = gras.TimeTag.from_ticks(42)
+ t1 = gras.TimeTag.from_ticks(-1000)
+ t2 = t0 - t1
+ self.assertEqual(t2.to_ticks(), (42 + 1000))
+
+ t3 = gras.TimeTag.from_ticks(99)
+ t4 = gras.TimeTag.from_ticks(-98)
+ t3 -= t4
+ self.assertEqual(t3.to_ticks(), (99 + 98))
+
+ def test_time_tag_pmc_foo(self):
+ time_us_now = long(time.time()*1e6)
+ t0 = gras.TimeTag.from_ticks(time_us_now, 1e6)
+
+ #test the validity of the time tuple
+ p0 = t0.to_pmc()
+ tp = p0()
+ full = long(time_us_now/1e6)
+ self.assertEqual(tp[0], full)
+ delta = time_us_now - full*long(1e6)
+ self.assertEqual(tp[1], (delta/1e6))
+
+ #and check if it loops back
+ t1 = gras.TimeTag.from_pmc(p0)
+ self.assertEqual(t0, t1)
+
+if __name__ == '__main__':
+ unittest.main()