summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gr-pdu/examples/CMakeLists.txt1
-rw-r--r--gr-pdu/examples/pdu_tools_demo.grc377
-rw-r--r--gr-pdu/grc/pdu.tree.yml4
-rw-r--r--gr-pdu/grc/pdu_add_system_time.block.yml27
-rw-r--r--gr-pdu/grc/pdu_pdu_split.block.yml6
-rw-r--r--gr-pdu/grc/pdu_time_delta.block.yml31
-rw-r--r--gr-pdu/include/gnuradio/pdu/CMakeLists.txt2
-rw-r--r--gr-pdu/include/gnuradio/pdu/add_system_time.h43
-rw-r--r--gr-pdu/include/gnuradio/pdu/time_delta.h50
-rw-r--r--gr-pdu/lib/CMakeLists.txt2
-rw-r--r--gr-pdu/lib/add_system_time_impl.cc68
-rw-r--r--gr-pdu/lib/add_system_time_impl.h35
-rw-r--r--gr-pdu/lib/pdu_split_impl.cc10
-rw-r--r--gr-pdu/lib/pdu_split_impl.h2
-rw-r--r--gr-pdu/lib/time_delta_impl.cc108
-rw-r--r--gr-pdu/lib/time_delta_impl.h49
-rw-r--r--gr-pdu/python/pdu/bindings/CMakeLists.txt2
-rw-r--r--gr-pdu/python/pdu/bindings/add_system_time_python.cc45
-rw-r--r--gr-pdu/python/pdu/bindings/docstrings/add_system_time_pydoc_template.h21
-rw-r--r--gr-pdu/python/pdu/bindings/docstrings/time_delta_pydoc_template.h21
-rw-r--r--gr-pdu/python/pdu/bindings/python_bindings.cc4
-rw-r--r--gr-pdu/python/pdu/bindings/time_delta_python.cc46
-rwxr-xr-xgr-pdu/python/pdu/qa_add_system_time.py71
-rw-r--r--gr-pdu/python/pdu/qa_pdu_split.py16
-rwxr-xr-xgr-pdu/python/pdu/qa_time_delta.py85
25 files changed, 1108 insertions, 18 deletions
diff --git a/gr-pdu/examples/CMakeLists.txt b/gr-pdu/examples/CMakeLists.txt
index c3f1df7c0d..cd0750ca63 100644
--- a/gr-pdu/examples/CMakeLists.txt
+++ b/gr-pdu/examples/CMakeLists.txt
@@ -7,6 +7,7 @@
install(
FILES
+ pdu_tools_demo.grc
DESTINATION ${GR_PKG_DATA_DIR}/examples/pdu
)
diff --git a/gr-pdu/examples/pdu_tools_demo.grc b/gr-pdu/examples/pdu_tools_demo.grc
new file mode 100644
index 0000000000..9e5c628a3a
--- /dev/null
+++ b/gr-pdu/examples/pdu_tools_demo.grc
@@ -0,0 +1,377 @@
+options:
+ parameters:
+ author: J. Gilbert
+ catch_exceptions: 'True'
+ category: '[GRC Hier Blocks]'
+ cmake_opt: ''
+ comment: ''
+ copyright: 2021 J. Gilbert
+ description: Example usage of a range of PDU tools.
+ gen_cmake: 'On'
+ gen_linking: dynamic
+ generate_options: qt_gui
+ hier_block_src_path: '.:'
+ id: pdu_tools_demo
+ max_nouts: '0'
+ output_language: python
+ placement: (0,0)
+ qt_qss_theme: ''
+ realtime_scheduling: ''
+ run: 'True'
+ run_command: '{python} -u {filename}'
+ run_options: prompt
+ sizing_mode: fixed
+ thread_safe_setters: ''
+ title: PDU Tools Example
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [24, 20.0]
+ rotation: 0
+ state: enabled
+
+blocks:
+- name: interval
+ id: variable
+ parameters:
+ comment: ''
+ value: '0.400'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [456, 20.0]
+ rotation: 0
+ state: true
+- name: timekey
+ id: variable
+ parameters:
+ comment: ''
+ value: pmt.intern("SYS_TIME")
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [240, 460.0]
+ rotation: 0
+ state: true
+- name: val
+ id: variable_qtgui_range
+ parameters:
+ comment: ''
+ gui_hint: ''
+ label: KEY1 Metadata Value
+ min_len: '200'
+ orient: QtCore.Qt.Horizontal
+ rangeType: float
+ start: '0'
+ step: '0.1'
+ stop: '500.0'
+ value: '123.4'
+ widget: counter_slider
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [216, 20.0]
+ rotation: 0
+ state: true
+- name: blocks_message_debug_0
+ id: blocks_message_debug
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: Print out the metadata dictionary.
+ en_uvec: 'True'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [584, 728.0]
+ rotation: 0
+ state: true
+- name: blocks_message_strobe_0
+ id: blocks_message_strobe
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: Emits pmt.PMT_T
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ msg: pmt.PMT_T
+ period: int(interval*1000)
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [56, 252.0]
+ rotation: 0
+ state: true
+- name: note_0
+ id: note
+ parameters:
+ alias: ''
+ comment: 'Normally, some PDU processing
+
+ would be done here for the time
+
+ benchmarks to be applied over.'
+ note: README
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [512, 532.0]
+ rotation: 0
+ state: true
+- name: note_0_0
+ id: note
+ parameters:
+ alias: ''
+ comment: 'This flowgraph highlights a number of useful PDU tools together.
+
+ This flowgraph is not intended to do anything particularily useful
+
+ other than serve as a reference / demonstration.'
+ note: SUMMARY
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [616, 20.0]
+ rotation: 0
+ state: true
+- name: pdu_add_system_time_0
+ id: pdu_add_system_time
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: 'This block will add the system
+
+ time to the PDU metadata as a
+
+ double precision float. The time
+
+ key can be set by the user.'
+ key: timekey
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [240, 532.0]
+ rotation: 0
+ state: true
+- name: pdu_pdu_set_0
+ id: pdu_pdu_set
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: 'This block will add a key/value pair
+
+ to the PDU metadata dictionary.'
+ k: pmt.intern("KEY1")
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ v: pmt.from_double(val)
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [720, 252.0]
+ rotation: 0
+ state: true
+- name: pdu_pdu_split_0
+ id: pdu_pdu_split
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: 'The PDU will be split into its metadata dictionary
+
+ and uniform vector, which will then be emitted as
+
+ dict/uvec PMT messages respectively.'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ pass_empty: 'False'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [280, 808.0]
+ rotation: 0
+ state: true
+- name: pdu_random_pdu_0
+ id: pdu_random_pdu
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: 'This block will emit a random PDU with
+
+ an empty dictionary each time it
+
+ receives an input message. The input
+
+ message composition does not matter.'
+ length_modulo: '256'
+ mask: '0xFF'
+ maxoutbuf: '0'
+ maxsize: '4096'
+ minoutbuf: '0'
+ minsize: '2048'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [320, 236.0]
+ rotation: 0
+ state: true
+- name: pdu_time_delta_0
+ id: pdu_time_delta
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: "This block calculates the ms that have \nelapsed between when the time\
+ \ key\nwas added and now, and print statistics\nwhen the flowgraph stops."
+ delta_key: pmt.intern("time_delta_ms")
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ time_key: timekey
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [752, 524.0]
+ rotation: 0
+ state: true
+- name: qtgui_time_raster_sink_x_0
+ id: qtgui_time_raster_sink_x
+ parameters:
+ affinity: ''
+ alias: ''
+ alpha1: '1.0'
+ alpha10: '1.0'
+ alpha2: '1.0'
+ alpha3: '1.0'
+ alpha4: '1.0'
+ alpha5: '1.0'
+ alpha6: '1.0'
+ alpha7: '1.0'
+ alpha8: '1.0'
+ alpha9: '1.0'
+ axislabels: 'True'
+ color1: '0'
+ color10: '0'
+ color2: '0'
+ color3: '0'
+ color4: '0'
+ color5: '0'
+ color6: '0'
+ color7: '0'
+ color8: '0'
+ color9: '0'
+ comment: Colors for fun.
+ grid: 'False'
+ gui_hint: ''
+ label1: ''
+ label10: ''
+ label2: ''
+ label3: ''
+ label4: ''
+ label5: ''
+ label6: ''
+ label7: ''
+ label8: ''
+ label9: ''
+ mult: '[]'
+ name: '"This Plot Is Not Meaningful"'
+ ncols: '128'
+ nconnections: '1'
+ nrows: '256'
+ offset: '[]'
+ samp_rate: '1'
+ type: msg_byte
+ update_time: '0.10'
+ x_end_value: '0.0'
+ x_label: '""'
+ x_start_value: '0.0'
+ y_end_value: '0.0'
+ y_label: '""'
+ y_start_value: '0.0'
+ zmax: '127'
+ zmin: '-128'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [904, 820.0]
+ rotation: 0
+ state: true
+- name: virtual_sink_0
+ id: virtual_sink
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: x
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1048, 260.0]
+ rotation: 0
+ state: true
+- name: virtual_sink_1
+ id: virtual_sink
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: y
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1056, 532.0]
+ rotation: 0
+ state: true
+- name: virtual_source_0
+ id: virtual_source
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: x
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [48, 532.0]
+ rotation: 0
+ state: true
+- name: virtual_source_1
+ id: virtual_source
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: y
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [32, 820.0]
+ rotation: 0
+ state: true
+
+connections:
+- [blocks_message_strobe_0, strobe, pdu_random_pdu_0, generate]
+- [pdu_add_system_time_0, pdu, pdu_time_delta_0, pdu]
+- [pdu_pdu_set_0, pdus, virtual_sink_0, '0']
+- [pdu_pdu_split_0, dict, blocks_message_debug_0, print]
+- [pdu_pdu_split_0, vec, qtgui_time_raster_sink_x_0, in]
+- [pdu_random_pdu_0, pdus, pdu_pdu_set_0, pdus]
+- [pdu_time_delta_0, pdu, virtual_sink_1, '0']
+- [virtual_source_0, '0', pdu_add_system_time_0, pdu]
+- [virtual_source_1, '0', pdu_pdu_split_0, pdu]
+
+metadata:
+ file_format: 1
diff --git a/gr-pdu/grc/pdu.tree.yml b/gr-pdu/grc/pdu.tree.yml
index da988d007d..2c1d2a2c9c 100644
--- a/gr-pdu/grc/pdu.tree.yml
+++ b/gr-pdu/grc/pdu.tree.yml
@@ -1,5 +1,6 @@
'[Core]':
-- Message Tools:
+- PDU Tools:
+ - pdu_add_system_time
- pdu_pdu_filter
- pdu_pdu_remove
- pdu_pdu_set
@@ -7,3 +8,4 @@
- pdu_pdu_to_tagged_stream
- pdu_tagged_stream_to_pdu
- pdu_random_pdu
+ - pdu_time_delta
diff --git a/gr-pdu/grc/pdu_add_system_time.block.yml b/gr-pdu/grc/pdu_add_system_time.block.yml
new file mode 100644
index 0000000000..e0c9be1aee
--- /dev/null
+++ b/gr-pdu/grc/pdu_add_system_time.block.yml
@@ -0,0 +1,27 @@
+id: pdu_add_system_time
+label: Add System Time
+flags: [ python ]
+
+parameters:
+- id: key
+ label: Key
+ dtype: raw
+ default: pmt.intern("system_time")
+
+inputs:
+- domain: message
+ id: pdu
+ optional: true
+
+outputs:
+- domain: message
+ id: pdu
+ optional: true
+
+templates:
+ imports: |-
+ from gnuradio import pdu
+ import pmt
+ make: pdu.add_system_time(${key})
+
+file_format: 1
diff --git a/gr-pdu/grc/pdu_pdu_split.block.yml b/gr-pdu/grc/pdu_pdu_split.block.yml
index 1f9ca1a75f..6dcd8f76e4 100644
--- a/gr-pdu/grc/pdu_pdu_split.block.yml
+++ b/gr-pdu/grc/pdu_pdu_split.block.yml
@@ -1,6 +1,6 @@
id: pdu_pdu_split
label: PDU Split
-category: '[Core]/PDU'
+flags: [ python ]
parameters:
- id: pass_empty
@@ -11,14 +11,14 @@ parameters:
inputs:
- domain: message
- id: pdus
+ id: pdu
outputs:
- domain: message
id: dict
optional: true
- domain: message
- id: data
+ id: vec
optional: true
templates:
diff --git a/gr-pdu/grc/pdu_time_delta.block.yml b/gr-pdu/grc/pdu_time_delta.block.yml
new file mode 100644
index 0000000000..98aa8f3d63
--- /dev/null
+++ b/gr-pdu/grc/pdu_time_delta.block.yml
@@ -0,0 +1,31 @@
+id: pdu_time_delta
+label: Time Delta
+flags: [ python ]
+
+parameters:
+- id: delta_key
+ label: Time Delta Key
+ dtype: raw
+ default: pmt.intern("time_delta_ms")
+- id: time_key
+ label: Time Key
+ dtype: raw
+ default: pmt.intern("system_time")
+
+inputs:
+- domain: message
+ id: pdu
+ optional: true
+
+outputs:
+- domain: message
+ id: pdu
+ optional: true
+
+templates:
+ imports: |-
+ from gnuradio import pdu
+ import pmt
+ make: pdu.time_delta(${delta_key}, ${time_key})
+
+file_format: 1
diff --git a/gr-pdu/include/gnuradio/pdu/CMakeLists.txt b/gr-pdu/include/gnuradio/pdu/CMakeLists.txt
index 987a54f45b..c3d626a3be 100644
--- a/gr-pdu/include/gnuradio/pdu/CMakeLists.txt
+++ b/gr-pdu/include/gnuradio/pdu/CMakeLists.txt
@@ -10,6 +10,7 @@
########################################################################
install(FILES
api.h
+ add_system_time.h
pdu_filter.h
pdu_remove.h
pdu_set.h
@@ -17,6 +18,7 @@ install(FILES
pdu_to_tagged_stream.h
random_pdu.h
tagged_stream_to_pdu.h
+ time_delta.h
DESTINATION ${GR_INCLUDE_DIR}/gnuradio/pdu
)
diff --git a/gr-pdu/include/gnuradio/pdu/add_system_time.h b/gr-pdu/include/gnuradio/pdu/add_system_time.h
new file mode 100644
index 0000000000..b8a6037bad
--- /dev/null
+++ b/gr-pdu/include/gnuradio/pdu/add_system_time.h
@@ -0,0 +1,43 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_PDU_ADD_SYSTEM_TIME_H
+#define INCLUDED_PDU_ADD_SYSTEM_TIME_H
+
+#include <gnuradio/block.h>
+#include <gnuradio/pdu/api.h>
+
+namespace gr {
+namespace pdu {
+
+/*!
+ * \brief Adds system time to a PDU's metadata
+ * \ingroup debug_tools_blk
+ * \ingroup pdu_blk
+ *
+ * Adds a user specified key to PDU dict containing the boost system time in seconds
+ * since unix epoch.
+ */
+class PDU_API add_system_time : virtual public gr::block
+{
+public:
+ typedef std::shared_ptr<add_system_time> sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of pdu::add_system_time.
+ *
+ * @param key - key to use for system time metadata field
+ */
+ static sptr make(const pmt::pmt_t key);
+};
+
+} // namespace pdu
+} // namespace gr
+
+#endif /* INCLUDED_PDU_ADD_SYSTEM_TIME_H */
diff --git a/gr-pdu/include/gnuradio/pdu/time_delta.h b/gr-pdu/include/gnuradio/pdu/time_delta.h
new file mode 100644
index 0000000000..963877f5db
--- /dev/null
+++ b/gr-pdu/include/gnuradio/pdu/time_delta.h
@@ -0,0 +1,50 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_PDU_TIME_DELTA_H
+#define INCLUDED_PDU_TIME_DELTA_H
+
+#include <gnuradio/block.h>
+#include <gnuradio/pdu/api.h>
+
+namespace gr {
+namespace pdu {
+
+/*!
+ * \brief Compute system time differences and provide statistics upon completion.
+ * \ingroup debug_tools_blk
+ * \ingroup pdu_blk
+ *
+ * This block computes the difference between the current system time and a defined PDU
+ * key previously added (e.g.: by by the add_system_time block). This is useful for
+ * benchmarking PDU operations. The block also maintians running estimates of mean and
+ * variance, accessible by getters and printed when the flowgraph is stopped.
+ */
+class PDU_API time_delta : virtual public gr::block
+{
+public:
+ typedef std::shared_ptr<time_delta> sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of pdu::time_delta.
+ *
+ * @param delta_key - key to use for time delta metadata field
+ * @param time_key - key to use as reference time for delta
+ */
+ static sptr make(const pmt::pmt_t delta_key, const pmt::pmt_t time_key);
+
+ virtual double get_variance(void) = 0;
+ virtual double get_mean(void) = 0;
+ virtual void reset_stats(void) = 0;
+};
+
+} // namespace pdu
+} // namespace gr
+
+#endif /* INCLUDED_PDU_TIME_DELTA_H */
diff --git a/gr-pdu/lib/CMakeLists.txt b/gr-pdu/lib/CMakeLists.txt
index 7de8a52bf8..cd071cf73f 100644
--- a/gr-pdu/lib/CMakeLists.txt
+++ b/gr-pdu/lib/CMakeLists.txt
@@ -9,6 +9,7 @@
# Setup library
########################################################################
add_library(gnuradio-pdu
+ add_system_time_impl.cc
pdu_filter_impl.cc
pdu_remove_impl.cc
pdu_set_impl.cc
@@ -16,6 +17,7 @@ add_library(gnuradio-pdu
pdu_to_tagged_stream_impl.cc
random_pdu_impl.cc
tagged_stream_to_pdu_impl.cc
+ time_delta_impl.cc
)
#Add Windows DLL resource file if using MSVC
diff --git a/gr-pdu/lib/add_system_time_impl.cc b/gr-pdu/lib/add_system_time_impl.cc
new file mode 100644
index 0000000000..50cebc634a
--- /dev/null
+++ b/gr-pdu/lib/add_system_time_impl.cc
@@ -0,0 +1,68 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "add_system_time_impl.h"
+#include <gnuradio/io_signature.h>
+#include <gnuradio/pdu.h>
+
+namespace gr {
+namespace pdu {
+
+add_system_time::sptr add_system_time::make(const pmt::pmt_t key)
+{
+ return gnuradio::make_block_sptr<add_system_time_impl>(key);
+}
+
+/*
+ * The private constructor
+ */
+add_system_time_impl::add_system_time_impl(const pmt::pmt_t key)
+ : gr::block("add_system_time",
+ gr::io_signature::make(0, 0, 0),
+ gr::io_signature::make(0, 0, 0)),
+ d_name(pmt::symbol_to_string(key)),
+ d_epoch(boost::gregorian::date(1970, 1, 1)),
+ d_key(key)
+{
+ // boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
+ // d_epoch = epoch;
+
+ message_port_register_in(msgport_names::pdu());
+ set_msg_handler(msgport_names::pdu(),
+ [this](const pmt::pmt_t& msg) { this->handle_pdu(msg); });
+ message_port_register_out(msgport_names::pdu());
+}
+
+/*
+ * Our virtual destructor.
+ */
+add_system_time_impl::~add_system_time_impl() {}
+
+void add_system_time_impl::handle_pdu(const pmt::pmt_t& pdu)
+{
+ // make sure the message is a PDU
+ if (!(pmt::is_pdu(pdu))) {
+ GR_LOG_WARN(d_logger, "Message received is not a PDU, dropping");
+ return;
+ }
+
+ pmt::pmt_t meta = pmt::car(pdu);
+
+ // append time and publish
+ double t_now((boost::get_system_time() - d_epoch).total_microseconds() / 1000000.0);
+ meta = pmt::dict_add(meta, d_key, pmt::from_double(t_now));
+ message_port_pub(msgport_names::pdu(), pmt::cons(meta, pmt::cdr(pdu)));
+}
+
+} /* namespace pdu */
+} /* namespace gr */
diff --git a/gr-pdu/lib/add_system_time_impl.h b/gr-pdu/lib/add_system_time_impl.h
new file mode 100644
index 0000000000..722bd112af
--- /dev/null
+++ b/gr-pdu/lib/add_system_time_impl.h
@@ -0,0 +1,35 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_PDU_ADD_SYSTEM_TIME_IMPL_H
+#define INCLUDED_PDU_ADD_SYSTEM_TIME_IMPL_H
+
+#include <gnuradio/pdu/add_system_time.h>
+
+namespace gr {
+namespace pdu {
+
+class add_system_time_impl : public add_system_time
+{
+private:
+ std::string d_name;
+ const boost::posix_time::ptime d_epoch;
+ pmt::pmt_t d_key;
+
+ void handle_pdu(const pmt::pmt_t& pdu);
+
+public:
+ add_system_time_impl(const pmt::pmt_t key);
+ ~add_system_time_impl() override;
+};
+
+} // namespace pdu
+} // namespace gr
+
+#endif /* INCLUDED_PDU_ADD_SYSTEM_TIME_IMPL_H */
diff --git a/gr-pdu/lib/pdu_split_impl.cc b/gr-pdu/lib/pdu_split_impl.cc
index 678e650ee2..8eb14bfffb 100644
--- a/gr-pdu/lib/pdu_split_impl.cc
+++ b/gr-pdu/lib/pdu_split_impl.cc
@@ -28,18 +28,18 @@ pdu_split_impl::pdu_split_impl(const bool pass_empty_data)
: gr::block("pdu_split", io_signature::make(0, 0, 0), io_signature::make(0, 0, 0)),
d_pass_empty_data(pass_empty_data)
{
- message_port_register_in(msgport_names::pdus());
- set_msg_handler(msgport_names::pdus(),
- [this](pmt::pmt_t msg) { this->handle_pdu(msg); });
+ message_port_register_in(msgport_names::pdu());
+ set_msg_handler(msgport_names::pdu(),
+ [this](const pmt::pmt_t& msg) { this->handle_pdu(msg); });
message_port_register_out(msgport_names::dict());
message_port_register_out(msgport_names::vec());
}
pdu_split_impl::~pdu_split_impl() {}
-void pdu_split_impl::handle_pdu(pmt::pmt_t pdu)
+void pdu_split_impl::handle_pdu(const pmt::pmt_t& pdu)
{
- // make sure the data is a PDU
+ // make sure the message is a PDU
if (!(pmt::is_pdu(pdu))) {
GR_LOG_WARN(d_logger, "Message received is not a PDU, dropping");
return;
diff --git a/gr-pdu/lib/pdu_split_impl.h b/gr-pdu/lib/pdu_split_impl.h
index cf26b5055e..d198e2c8d0 100644
--- a/gr-pdu/lib/pdu_split_impl.h
+++ b/gr-pdu/lib/pdu_split_impl.h
@@ -27,7 +27,7 @@ private:
*
* \param pdu A PDU message passed from the scheduler's message handling.
*/
- void handle_pdu(pmt::pmt_t pdu);
+ void handle_pdu(const pmt::pmt_t& pdu);
public:
pdu_split_impl(const bool pass_empty_data);
diff --git a/gr-pdu/lib/time_delta_impl.cc b/gr-pdu/lib/time_delta_impl.cc
new file mode 100644
index 0000000000..e7213dc0ff
--- /dev/null
+++ b/gr-pdu/lib/time_delta_impl.cc
@@ -0,0 +1,108 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "time_delta_impl.h"
+#include <gnuradio/io_signature.h>
+#include <gnuradio/pdu.h>
+
+namespace gr {
+namespace pdu {
+
+time_delta::sptr time_delta::make(const pmt::pmt_t delta_key, const pmt::pmt_t time_key)
+{
+ return gnuradio::make_block_sptr<time_delta_impl>(delta_key, time_key);
+}
+
+time_delta_impl::time_delta_impl(const pmt::pmt_t delta_key, const pmt::pmt_t time_key)
+ : gr::block("time_delta", io_signature::make(0, 0, 0), io_signature::make(0, 0, 0)),
+ d_name(pmt::symbol_to_string(delta_key)),
+ d_delta_key(delta_key),
+ d_time_key(time_key),
+ d_epoch(boost::gregorian::date(1970, 1, 1)),
+ d_mean(0.0),
+ d_var_acc(0.0),
+ d_var(0.0),
+ d_n(0)
+{
+ // boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
+ // d_epoch = epoch;
+ message_port_register_in(msgport_names::pdu());
+ set_msg_handler(msgport_names::pdu(),
+ [this](const pmt::pmt_t& msg) { this->handle_pdu(msg); });
+ message_port_register_out(msgport_names::pdu());
+}
+
+time_delta_impl::~time_delta_impl() {}
+
+bool time_delta_impl::stop()
+{
+ GR_LOG_NOTICE(d_logger,
+ boost::format("Statistics for %s: Mean = %0.6f ms, Var = %0.6f ms") %
+ d_name % d_mean % get_variance());
+ return true;
+}
+
+void time_delta_impl::reset_stats()
+{
+ // do not allow statistics to be reset while they are being accessed
+ gr::thread::scoped_lock l(d_mutex);
+
+ d_n = 0;
+ d_mean = 0;
+ d_var_acc = 0;
+ d_var = 0;
+}
+
+void time_delta_impl::update_stats(double x)
+{
+ // uses Welford's Method to calculate the running variance
+ double prev_mean = d_mean;
+ d_n++;
+ d_mean = d_mean + (x - d_mean) / d_n;
+ d_var_acc += (x - d_mean) * (x - prev_mean);
+ d_var = d_var_acc / (d_n - 1);
+}
+
+void time_delta_impl::handle_pdu(const pmt::pmt_t& pdu)
+{
+ // make sure the message is a PDU
+ if (!(pmt::is_pdu(pdu))) {
+ GR_LOG_WARN(d_logger, "Message received is not a PDU, dropping");
+ return;
+ }
+
+ gr::thread::scoped_lock l(d_mutex);
+
+ double t_now((boost::get_system_time() - d_epoch).total_microseconds() / 1000000.0);
+
+ pmt::pmt_t meta = pmt::car(pdu);
+ pmt::pmt_t sys_time_pmt = pmt::dict_ref(meta, d_time_key, pmt::PMT_NIL);
+ if (!pmt::is_real(sys_time_pmt)) {
+ GR_LOG_INFO(d_logger,
+ boost::format("PDU received with no system time at %f") % t_now);
+ return;
+ }
+
+ double pdu_time = pmt::to_double(sys_time_pmt);
+ double time_delta_ms = (t_now - pdu_time) * 1000.0;
+ GR_LOG_DEBUG(d_logger,
+ boost::format("%s PDU received at %f with time delta %f milliseconds") %
+ d_name % t_now % time_delta_ms);
+ update_stats(time_delta_ms);
+
+ meta = pmt::dict_add(meta, d_delta_key, pmt::from_double(time_delta_ms));
+ message_port_pub(msgport_names::pdu(), pmt::cons(meta, pmt::cdr(pdu)));
+}
+
+} /* namespace pdu */
+} /* namespace gr */
diff --git a/gr-pdu/lib/time_delta_impl.h b/gr-pdu/lib/time_delta_impl.h
new file mode 100644
index 0000000000..621964b380
--- /dev/null
+++ b/gr-pdu/lib/time_delta_impl.h
@@ -0,0 +1,49 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef INCLUDED_PDU_TIME_DELTA_IMPL_H
+#define INCLUDED_PDU_TIME_DELTA_IMPL_H
+
+#include <gnuradio/pdu/time_delta.h>
+
+namespace gr {
+namespace pdu {
+
+class time_delta_impl : public time_delta
+{
+private:
+ std::string d_name;
+ const pmt::pmt_t d_delta_key;
+ const pmt::pmt_t d_time_key;
+ const boost::posix_time::ptime d_epoch;
+
+ gr::thread::mutex d_mutex;
+
+ // statistic tracking
+ double d_mean, d_var_acc, d_var;
+ size_t d_n;
+
+ void update_stats(double x);
+ void handle_pdu(const pmt::pmt_t& pdu);
+
+public:
+ time_delta_impl(const pmt::pmt_t delta_key, const pmt::pmt_t time_key);
+ ~time_delta_impl() override;
+
+ bool stop() override;
+
+ double get_variance(void) override { return d_var; };
+ double get_mean(void) override { return d_mean; };
+ void reset_stats(void) override;
+};
+
+} // namespace pdu
+} // namespace gr
+
+#endif /* INCLUDED_PDU_TIME_DELTA_IMPL_H */
diff --git a/gr-pdu/python/pdu/bindings/CMakeLists.txt b/gr-pdu/python/pdu/bindings/CMakeLists.txt
index 79e33229e0..79334e2440 100644
--- a/gr-pdu/python/pdu/bindings/CMakeLists.txt
+++ b/gr-pdu/python/pdu/bindings/CMakeLists.txt
@@ -5,6 +5,7 @@ include(GrPybind)
########################################################################
list(APPEND pdu_python_files
+ add_system_time_python.cc
pdu_filter_python.cc
pdu_remove_python.cc
pdu_set_python.cc
@@ -12,6 +13,7 @@ list(APPEND pdu_python_files
pdu_to_tagged_stream_python.cc
random_pdu_python.cc
tagged_stream_to_pdu_python.cc
+ time_delta_python.cc
python_bindings.cc)
GR_PYBIND_MAKE_CHECK_HASH(pdu
diff --git a/gr-pdu/python/pdu/bindings/add_system_time_python.cc b/gr-pdu/python/pdu/bindings/add_system_time_python.cc
new file mode 100644
index 0000000000..97b3d1d660
--- /dev/null
+++ b/gr-pdu/python/pdu/bindings/add_system_time_python.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2021 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+/***********************************************************************************/
+/* This file is automatically generated using bindtool and can be manually edited */
+/* The following lines can be configured to regenerate this file during cmake */
+/* If manual edits are made, the following tags should be modified accordingly. */
+/* BINDTOOL_GEN_AUTOMATIC(0) */
+/* BINDTOOL_USE_PYGCCXML(0) */
+/* BINDTOOL_HEADER_FILE(add_system_time.h) */
+/* BINDTOOL_HEADER_FILE_HASH(3abd057660e6652a1bb162bc47c0db73) */
+/***********************************************************************************/
+
+#include <pybind11/complex.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+namespace py = pybind11;
+
+#include <gnuradio/pdu/add_system_time.h>
+// pydoc.h is automatically generated in the build directory
+#include <add_system_time_pydoc.h>
+
+void bind_add_system_time(py::module& m)
+{
+
+ using add_system_time = gr::pdu::add_system_time;
+
+
+ py::class_<add_system_time,
+ gr::block,
+ gr::basic_block,
+ std::shared_ptr<add_system_time>>(m, "add_system_time", D(add_system_time))
+
+ .def(py::init(&add_system_time::make), py::arg("key"), D(add_system_time, make))
+
+
+ ;
+}
diff --git a/gr-pdu/python/pdu/bindings/docstrings/add_system_time_pydoc_template.h b/gr-pdu/python/pdu/bindings/docstrings/add_system_time_pydoc_template.h
new file mode 100644
index 0000000000..3de2f612dc
--- /dev/null
+++ b/gr-pdu/python/pdu/bindings/docstrings/add_system_time_pydoc_template.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+#include "pydoc_macros.h"
+#define D(...) DOC(gr, pdu, __VA_ARGS__)
+/*
+ This file contains placeholders for docstrings for the Python bindings.
+ Do not edit! These were automatically extracted during the binding process
+ and will be overwritten during the build process
+ */
+
+
+static const char* __doc_gr_pdu_add_system_time = R"doc()doc";
+
+
+static const char* __doc_gr_pdu_add_system_time_make = R"doc()doc";
diff --git a/gr-pdu/python/pdu/bindings/docstrings/time_delta_pydoc_template.h b/gr-pdu/python/pdu/bindings/docstrings/time_delta_pydoc_template.h
new file mode 100644
index 0000000000..7c3b25060f
--- /dev/null
+++ b/gr-pdu/python/pdu/bindings/docstrings/time_delta_pydoc_template.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+#include "pydoc_macros.h"
+#define D(...) DOC(gr, pdu, __VA_ARGS__)
+/*
+ This file contains placeholders for docstrings for the Python bindings.
+ Do not edit! These were automatically extracted during the binding process
+ and will be overwritten during the build process
+ */
+
+
+static const char* __doc_gr_pdu_time_delta = R"doc()doc";
+
+
+static const char* __doc_gr_pdu_time_delta_make = R"doc()doc";
diff --git a/gr-pdu/python/pdu/bindings/python_bindings.cc b/gr-pdu/python/pdu/bindings/python_bindings.cc
index 3c311e5848..0739908789 100644
--- a/gr-pdu/python/pdu/bindings/python_bindings.cc
+++ b/gr-pdu/python/pdu/bindings/python_bindings.cc
@@ -15,6 +15,7 @@
namespace py = pybind11;
+void bind_add_system_time(py::module&);
void bind_pdu_filter(py::module&);
void bind_pdu_remove(py::module&);
void bind_pdu_set(py::module&);
@@ -22,6 +23,7 @@ void bind_pdu_split(py::module&);
void bind_pdu_to_tagged_stream(py::module&);
void bind_random_pdu(py::module&);
void bind_tagged_stream_to_pdu(py::module&);
+void bind_time_delta(py::module&);
// We need this hack because import_array() returns NULL
// for newer Python versions.
@@ -42,6 +44,7 @@ PYBIND11_MODULE(pdu_python, m)
// Allow access to base block methods
py::module::import("gnuradio.gr");
+ bind_add_system_time(m);
bind_pdu_filter(m);
bind_pdu_remove(m);
bind_pdu_set(m);
@@ -49,4 +52,5 @@ PYBIND11_MODULE(pdu_python, m)
bind_pdu_to_tagged_stream(m);
bind_random_pdu(m);
bind_tagged_stream_to_pdu(m);
+ bind_time_delta(m);
}
diff --git a/gr-pdu/python/pdu/bindings/time_delta_python.cc b/gr-pdu/python/pdu/bindings/time_delta_python.cc
new file mode 100644
index 0000000000..71790064ff
--- /dev/null
+++ b/gr-pdu/python/pdu/bindings/time_delta_python.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2021 Free Software Foundation, Inc.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+/***********************************************************************************/
+/* This file is automatically generated using bindtool and can be manually edited */
+/* The following lines can be configured to regenerate this file during cmake */
+/* If manual edits are made, the following tags should be modified accordingly. */
+/* BINDTOOL_GEN_AUTOMATIC(0) */
+/* BINDTOOL_USE_PYGCCXML(0) */
+/* BINDTOOL_HEADER_FILE(time_delta.h) */
+/* BINDTOOL_HEADER_FILE_HASH(292cf88806dbd063fba41550fc47a9b9) */
+/***********************************************************************************/
+
+#include <pybind11/complex.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+namespace py = pybind11;
+
+#include <gnuradio/pdu/time_delta.h>
+// pydoc.h is automatically generated in the build directory
+#include <time_delta_pydoc.h>
+
+void bind_time_delta(py::module& m)
+{
+
+ using time_delta = gr::pdu::time_delta;
+
+
+ py::class_<time_delta, gr::block, gr::basic_block, std::shared_ptr<time_delta>>(
+ m, "time_delta", D(time_delta))
+
+ .def(py::init(&time_delta::make),
+ py::arg("delta_key"),
+ py::arg("time_key"),
+ D(time_delta, make))
+
+
+ ;
+}
diff --git a/gr-pdu/python/pdu/qa_add_system_time.py b/gr-pdu/python/pdu/qa_add_system_time.py
new file mode 100755
index 0000000000..44d0ad4bef
--- /dev/null
+++ b/gr-pdu/python/pdu/qa_add_system_time.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python
+#
+# Copyright 2021 NTESS LLC.
+#
+# This file is part of GNU Radio
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+#
+
+
+from gnuradio import gr, gr_unittest, blocks, pdu
+import pmt
+import time
+
+
+class qa_add_system_time(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ self.add_sys_time = pdu.add_system_time(pmt.intern('systime'))
+ self.debug = blocks.message_debug()
+
+ # make connections
+ self.tb.msg_connect((self.add_sys_time, 'pdu'), (self.debug, 'store'))
+ self.tb.msg_connect((self.add_sys_time, 'pdu'), (self.debug, 'print'))
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_001_basic_io(self):
+ self.tb.start()
+ # provide two non PDU inputs and one PDU input
+ self.add_sys_time.to_basic_block()._post(pmt.intern("pdu"), pmt.intern("BAD PDU"))
+ self.add_sys_time.to_basic_block()._post(pmt.intern("pdu"),
+ pmt.cons(pmt.from_long(4), pmt.PMT_NIL))
+ self.add_sys_time.to_basic_block()._post(pmt.intern("pdu"),
+ pmt.cons(pmt.make_dict(), pmt.init_f32vector(1, [0.0])))
+ self.waitFor(lambda: self.debug.num_messages() >= 1, timeout=1.0, poll_interval=0.01)
+ self.tb.stop()
+ self.tb.wait()
+
+ # make sure we got one message and it has a systime key
+ self.assertEqual(1, self.debug.num_messages())
+ self.assertTrue(pmt.dict_has_key(pmt.car(self.debug.get_message(0)), pmt.intern('systime')))
+
+ def test_002_timing(self):
+ self.tb.start()
+
+ self.add_sys_time.to_basic_block()._post(pmt.intern("pdu"), pmt.intern("BAD PDU"))
+ self.add_sys_time.to_basic_block()._post(pmt.intern("pdu"),
+ pmt.cons(pmt.make_dict(), pmt.init_u8vector(1, [0])))
+ time.sleep(1.0) # wait for one second to provide a time difference between messages
+ self.add_sys_time.to_basic_block()._post(pmt.intern("pdu"),
+ pmt.cons(pmt.make_dict(), pmt.init_u8vector(1, [0])))
+ self.waitFor(lambda: self.debug.num_messages() == 2, timeout=1.0, poll_interval=0.01)
+ self.tb.stop()
+ self.tb.wait()
+
+ t0 = pmt.to_double(pmt.dict_ref(pmt.car(self.debug.get_message(0)),
+ pmt.intern("systime"),
+ pmt.from_double(0.0)))
+ t1 = pmt.to_double(pmt.dict_ref(pmt.car(self.debug.get_message(1)),
+ pmt.intern("systime"),
+ pmt.from_double(0.0)))
+ self.assertTrue(((t1 - t0) - 1) < 0.05) # should be sufficient tolerance
+
+
+if __name__ == '__main__':
+ gr_unittest.run(qa_add_system_time)
diff --git a/gr-pdu/python/pdu/qa_pdu_split.py b/gr-pdu/python/pdu/qa_pdu_split.py
index 76b60499a7..988fb6b164 100644
--- a/gr-pdu/python/pdu/qa_pdu_split.py
+++ b/gr-pdu/python/pdu/qa_pdu_split.py
@@ -35,10 +35,10 @@ class qa_pdu_split (gr_unittest.TestCase):
in_pdu = pmt.cons(in_meta1, pmt.init_u8vector(6, range(6)))
self.tb.start()
- split.to_basic_block()._post(pmt.intern("pdus"), pmt.intern("MALFORMED PDU"))
- split.to_basic_block()._post(pmt.intern("pdus"), pmt.cons(pmt.PMT_NIL, pmt.init_u8vector(2, range(2))))
- split.to_basic_block()._post(pmt.intern("pdus"), pmt.cons(in_meta2, pmt.init_u8vector(0, [])))
- split.to_basic_block()._post(pmt.intern("pdus"), in_pdu)
+ split.to_basic_block()._post(pmt.intern("pdu"), pmt.intern("MALFORMED PDU"))
+ split.to_basic_block()._post(pmt.intern("pdu"), pmt.cons(pmt.PMT_NIL, pmt.init_u8vector(2, range(2))))
+ split.to_basic_block()._post(pmt.intern("pdu"), pmt.cons(in_meta2, pmt.init_u8vector(0, [])))
+ split.to_basic_block()._post(pmt.intern("pdu"), in_pdu)
split.to_basic_block()._post(pmt.intern("system"), pmt.cons(pmt.intern("done"), pmt.from_long(1)))
self.waitFor(lambda: d1.num_messages() == 2, timeout=1.0, poll_interval=0.01)
self.waitFor(lambda: d2.num_messages() == 2, timeout=1.0, poll_interval=0.01)
@@ -64,10 +64,10 @@ class qa_pdu_split (gr_unittest.TestCase):
in_pdu = pmt.cons(in_meta1, pmt.init_u8vector(6, range(6)))
self.tb.start()
- split.to_basic_block()._post(pmt.intern("pdus"), pmt.intern("MALFORMED PDU"))
- split.to_basic_block()._post(pmt.intern("pdus"), pmt.cons(pmt.PMT_NIL, pmt.init_u8vector(2, range(2))))
- split.to_basic_block()._post(pmt.intern("pdus"), pmt.cons(in_meta2, pmt.init_u8vector(0, [])))
- split.to_basic_block()._post(pmt.intern("pdus"), in_pdu)
+ split.to_basic_block()._post(pmt.intern("pdu"), pmt.intern("MALFORMED PDU"))
+ split.to_basic_block()._post(pmt.intern("pdu"), pmt.cons(pmt.PMT_NIL, pmt.init_u8vector(2, range(2))))
+ split.to_basic_block()._post(pmt.intern("pdu"), pmt.cons(in_meta2, pmt.init_u8vector(0, [])))
+ split.to_basic_block()._post(pmt.intern("pdu"), in_pdu)
split.to_basic_block()._post(pmt.intern("system"), pmt.cons(pmt.intern("done"), pmt.from_long(1)))
self.waitFor(lambda: d1.num_messages() == 3, timeout=1.0, poll_interval=0.01)
self.waitFor(lambda: d2.num_messages() == 3, timeout=1.0, poll_interval=0.01)
diff --git a/gr-pdu/python/pdu/qa_time_delta.py b/gr-pdu/python/pdu/qa_time_delta.py
new file mode 100755
index 0000000000..0cb0f99020
--- /dev/null
+++ b/gr-pdu/python/pdu/qa_time_delta.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+#
+# Copyright 2021 NTESS LLC.
+#
+# This file is part of GNU Radio
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+#
+
+
+from gnuradio import gr, gr_unittest, blocks, pdu
+import pmt
+import numpy as np
+import time
+
+
+class qa_time_delta(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ self.time_delta = pdu.time_delta(pmt.intern("sys time delta (ms)"), pmt.intern("system_time"))
+ self.debug = blocks.message_debug()
+
+ self.tb.msg_connect((self.time_delta, 'pdu'), (self.debug, 'store'))
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_001_invalid_a(self):
+ self.tb.start()
+ self.time_delta.to_basic_block()._post(pmt.intern("pdu"), pmt.intern("NOT A PDU"))
+ time.sleep(0.01) # short delay to ensure the message is processed
+ self.tb.stop()
+ self.tb.wait()
+
+ # nothing should be produced in this case
+ self.assertEqual(0, self.debug.num_messages())
+
+ def test_001_invalid_b(self):
+ in_data = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1]
+ meta = pmt.dict_add(pmt.make_dict(), pmt.intern('sam'), pmt.from_double(25.1))
+ in_pdu = pmt.cons(meta, pmt.init_c32vector(len(in_data), in_data))
+
+ # set up fg
+ self.tb.start()
+ self.time_delta.to_basic_block()._post(pmt.intern("pdu"), in_pdu)
+ time.sleep(0.01) # short delay to ensure the message is processed
+ self.tb.stop()
+ self.tb.wait()
+
+ # nothing should be produced in this case
+ self.assertEqual(0, self.debug.num_messages())
+
+ def test_002_normal(self):
+ tnow = time.time()
+
+ in_data = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1]
+ meta = pmt.dict_add(pmt.make_dict(), pmt.intern('system_time'), pmt.from_double(tnow - 10.0))
+ in_pdu = pmt.cons(meta, pmt.init_c32vector(len(in_data), in_data))
+
+ e_data = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1]
+ e_meta = pmt.dict_add(pmt.make_dict(), pmt.intern('system_time'), pmt.from_double(tnow))
+ e_meta = pmt.dict_add(e_meta, pmt.intern('sys time delta (ms)'), pmt.from_double(10000.0))
+ e_pdu = pmt.cons(e_meta, pmt.init_c32vector(len(e_data), e_data))
+
+ # set up fg
+ self.tb.start()
+ self.time_delta.to_basic_block()._post(pmt.intern("pdu"), in_pdu)
+ self.waitFor(lambda: self.debug.num_messages() == 1, timeout=1.0, poll_interval=0.01)
+ self.tb.stop()
+ self.tb.wait()
+
+ # check data
+ self.assertEqual(1, self.debug.num_messages())
+ a_meta = pmt.car(self.debug.get_message(0))
+ time_tag = pmt.dict_ref(a_meta, pmt.intern("system_time"), pmt.PMT_NIL)
+ delta_tag = pmt.dict_ref(a_meta, pmt.intern("sys time delta (ms)"), pmt.PMT_NIL)
+ self.assertAlmostEqual(tnow, pmt.to_double(time_tag), delta=60)
+ self.assertAlmostEqual(10000, pmt.to_double(delta_tag), delta=10)
+
+
+if __name__ == '__main__':
+ gr_unittest.run(qa_time_delta)