summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Gilbert <jacob.gilbert@protonmail.com>2021-07-06 09:32:06 -0600
committermormj <34754695+mormj@users.noreply.github.com>2021-07-19 06:44:24 -0400
commit92b5eaaf091ccd6e064760f8dbdd9db321a7af2b (patch)
treee8b949d8c7ebbc2fe4bacc62a6e217ccb4defab8
parent4d3eab2a40379cd6073eaccee86ac10b442382eb (diff)
pdu: adding tags_to_pdu block
Signed-off-by: Jacob Gilbert <jacob.gilbert@protonmail.com>
-rw-r--r--gnuradio-runtime/include/gnuradio/pdu.h6
-rw-r--r--gnuradio-runtime/lib/pdu.cc20
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/pdu_pydoc_template.h12
-rw-r--r--gnuradio-runtime/python/gnuradio/gr/bindings/pdu_python.cc15
-rw-r--r--gr-pdu/examples/tags_to_pdu_example.grc840
-rw-r--r--gr-pdu/grc/pdu.tree.yml1
-rw-r--r--gr-pdu/grc/pdu_tags_to_pdu.block.yml134
-rw-r--r--gr-pdu/include/gnuradio/pdu/CMakeLists.txt1
-rw-r--r--gr-pdu/include/gnuradio/pdu/tags_to_pdu.h79
-rw-r--r--gr-pdu/lib/CMakeLists.txt1
-rw-r--r--gr-pdu/lib/tags_to_pdu_impl.cc429
-rw-r--r--gr-pdu/lib/tags_to_pdu_impl.h115
-rw-r--r--gr-pdu/python/pdu/bindings/CMakeLists.txt1
-rw-r--r--gr-pdu/python/pdu/bindings/docstrings/tags_to_pdu_pydoc_template.h15
-rw-r--r--gr-pdu/python/pdu/bindings/python_bindings.cc2
-rw-r--r--gr-pdu/python/pdu/bindings/tags_to_pdu_python.cc75
-rwxr-xr-xgr-pdu/python/pdu/qa_tags_to_pdu.py233
17 files changed, 1977 insertions, 2 deletions
diff --git a/gnuradio-runtime/include/gnuradio/pdu.h b/gnuradio-runtime/include/gnuradio/pdu.h
index b930f3d0df..bab3564789 100644
--- a/gnuradio-runtime/include/gnuradio/pdu.h
+++ b/gnuradio-runtime/include/gnuradio/pdu.h
@@ -18,6 +18,8 @@
namespace gr {
namespace metadata_keys {
GR_RUNTIME_API const pmt::pmt_t pdu_num();
+GR_RUNTIME_API const pmt::pmt_t rx_time();
+GR_RUNTIME_API const pmt::pmt_t sys_time();
GR_RUNTIME_API const pmt::pmt_t tx_eob();
GR_RUNTIME_API const pmt::pmt_t tx_time();
GR_RUNTIME_API const pmt::pmt_t tx_sob();
@@ -26,12 +28,14 @@ GR_RUNTIME_API const pmt::pmt_t tx_sob();
namespace msgport_names {
// static const PMT interned string getters for standard port names
GR_RUNTIME_API const pmt::pmt_t bpdu();
+GR_RUNTIME_API const pmt::pmt_t conf();
GR_RUNTIME_API const pmt::pmt_t cpdu();
+GR_RUNTIME_API const pmt::pmt_t detects();
GR_RUNTIME_API const pmt::pmt_t dict();
GR_RUNTIME_API const pmt::pmt_t fpdu();
GR_RUNTIME_API const pmt::pmt_t msg();
GR_RUNTIME_API const pmt::pmt_t pdu();
-GR_RUNTIME_API const pmt::pmt_t pdus(); // compatibility, use of pdu() preferred
+GR_RUNTIME_API const pmt::pmt_t pdus();
GR_RUNTIME_API const pmt::pmt_t vec();
} /* namespace msgport_names */
diff --git a/gnuradio-runtime/lib/pdu.cc b/gnuradio-runtime/lib/pdu.cc
index 7b75fbac1a..79a6de6c79 100644
--- a/gnuradio-runtime/lib/pdu.cc
+++ b/gnuradio-runtime/lib/pdu.cc
@@ -22,6 +22,16 @@ const pmt::pmt_t pdu_num()
static const pmt::pmt_t val = pmt::mp("pdu_num");
return val;
}
+const pmt::pmt_t rx_time()
+{
+ static const pmt::pmt_t val = pmt::mp("rx_time");
+ return val;
+}
+const pmt::pmt_t sys_time()
+{
+ static const pmt::pmt_t val = pmt::mp("sys_time");
+ return val;
+}
const pmt::pmt_t tx_eob()
{
static const pmt::pmt_t val = pmt::mp("tx_eob");
@@ -47,11 +57,21 @@ const pmt::pmt_t bpdu()
static const pmt::pmt_t val = pmt::mp("bpdu");
return val;
}
+const pmt::pmt_t conf()
+{
+ static const pmt::pmt_t val = pmt::mp("conf");
+ return val;
+}
const pmt::pmt_t cpdu()
{
static const pmt::pmt_t val = pmt::mp("cpdu");
return val;
}
+const pmt::pmt_t detects()
+{
+ static const pmt::pmt_t val = pmt::mp("detects");
+ return val;
+}
const pmt::pmt_t dict()
{
static const pmt::pmt_t val = pmt::mp("dict");
diff --git a/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/pdu_pydoc_template.h b/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/pdu_pydoc_template.h
index ebc11a8351..282d505b40 100644
--- a/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/pdu_pydoc_template.h
+++ b/gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/pdu_pydoc_template.h
@@ -18,6 +18,12 @@
static const char* __doc_gr_metadata_keys_pdu_num = R"doc()doc";
+static const char* __doc_gr_metadata_keys_rx_time = R"doc()doc";
+
+
+static const char* __doc_gr_metadata_keys_sys_time = R"doc()doc";
+
+
static const char* __doc_gr_metadata_keys_tx_eob = R"doc()doc";
@@ -30,9 +36,15 @@ static const char* __doc_gr_metadata_keys_tx_sob = R"doc()doc";
static const char* __doc_gr_msgport_names_bpdu = R"doc()doc";
+static const char* __doc_gr_msgport_names_conf = R"doc()doc";
+
+
static const char* __doc_gr_msgport_names_cpdu = R"doc()doc";
+static const char* __doc_gr_msgport_names_detects = R"doc()doc";
+
+
static const char* __doc_gr_msgport_names_dict = R"doc()doc";
diff --git a/gnuradio-runtime/python/gnuradio/gr/bindings/pdu_python.cc b/gnuradio-runtime/python/gnuradio/gr/bindings/pdu_python.cc
index 407a72a823..674c633ccf 100644
--- a/gnuradio-runtime/python/gnuradio/gr/bindings/pdu_python.cc
+++ b/gnuradio-runtime/python/gnuradio/gr/bindings/pdu_python.cc
@@ -14,7 +14,7 @@
/* BINDTOOL_GEN_AUTOMATIC(0) */
/* BINDTOOL_USE_PYGCCXML(0) */
/* BINDTOOL_HEADER_FILE(pdu.h) */
-/* BINDTOOL_HEADER_FILE_HASH(562b6c641c014996cd7a94b2252030d1) */
+/* BINDTOOL_HEADER_FILE_HASH(6772caeddffe60c0c16148f68d21654f) */
/***********************************************************************************/
#include <pybind11/complex.h>
@@ -48,6 +48,12 @@ void bind_pdu(py::module& m)
m_metadata_keys.def(
"pdu_num", &::gr::metadata_keys::pdu_num, D(metadata_keys, pdu_num));
+ m_metadata_keys.def(
+ "rx_time", &::gr::metadata_keys::rx_time, D(metadata_keys, rx_time));
+
+ m_metadata_keys.def(
+ "sys_time", &::gr::metadata_keys::sys_time, D(metadata_keys, sys_time));
+
m_metadata_keys.def("tx_eob", &::gr::metadata_keys::tx_eob, D(metadata_keys, tx_eob));
m_metadata_keys.def(
@@ -61,9 +67,16 @@ void bind_pdu(py::module& m)
m_msgport_names.def("bpdu", &::gr::msgport_names::bpdu, D(msgport_names, bpdu));
+ m_msgport_names.def("conf", &::gr::msgport_names::conf, D(msgport_names, conf));
+
+
m_msgport_names.def("cpdu", &::gr::msgport_names::cpdu, D(msgport_names, cpdu));
+ m_msgport_names.def(
+ "detects", &::gr::msgport_names::detects, D(msgport_names, detects));
+
+
m_msgport_names.def("dict", &::gr::msgport_names::dict, D(msgport_names, dict));
diff --git a/gr-pdu/examples/tags_to_pdu_example.grc b/gr-pdu/examples/tags_to_pdu_example.grc
new file mode 100644
index 0000000000..bbb48b433f
--- /dev/null
+++ b/gr-pdu/examples/tags_to_pdu_example.grc
@@ -0,0 +1,840 @@
+options:
+ parameters:
+ author: J. Gilbert
+ catch_exceptions: 'True'
+ category: '[GRC Hier Blocks]'
+ cmake_opt: ''
+ comment: ''
+ copyright: 2021 J. Gilbert
+ description: Simple example of Tags to PDU block usage
+ gen_cmake: 'On'
+ gen_linking: dynamic
+ generate_options: qt_gui
+ hier_block_src_path: '.:'
+ id: tags_to_pdu_example
+ 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: Tags to PDU Example Flowgraph
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [16, 12.0]
+ rotation: 0
+ state: enabled
+
+blocks:
+- name: samp_rate
+ id: variable
+ parameters:
+ comment: ''
+ value: '100000'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [200, 12.0]
+ rotation: 0
+ state: enabled
+- name: add
+ id: blocks_add_xx
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ num_inputs: '2'
+ type: complex
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1160, 104.0]
+ rotation: 0
+ state: true
+- name: blocks_delay_0
+ id: blocks_delay
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ delay: '500'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ num_ports: '1'
+ type: complex
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [248, 320.0]
+ rotation: 0
+ state: true
+- name: c2r
+ id: blocks_complex_to_real
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [224, 516.0]
+ rotation: 0
+ state: true
+- name: f2c
+ id: blocks_float_to_complex
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [672, 208.0]
+ rotation: 0
+ state: true
+- name: f2s
+ id: blocks_float_to_short
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ scale: '1'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [848, 388.0]
+ rotation: 0
+ state: true
+- name: fm
+ id: analog_frequency_modulator_fc
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ sensitivity: '2'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [768, 76.0]
+ rotation: 0
+ state: true
+- name: fns
+ id: analog_fastnoise_source_x
+ parameters:
+ affinity: ''
+ alias: ''
+ amp: '.01'
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ noise_type: analog.GR_GAUSSIAN
+ samples: '8192'
+ seed: '0'
+ type: complex
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [896, 188.0]
+ rotation: 0
+ state: true
+- name: m2
+ id: blocks_complex_to_mag_squared
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [224, 396.0]
+ rotation: 0
+ state: true
+- name: ma
+ id: blocks_moving_average_xx
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ length: '1000'
+ max_iter: '4000'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ scale: 1/1000.0
+ type: float
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [464, 364.0]
+ rotation: 0
+ state: true
+- name: msg_dbg
+ id: blocks_message_debug
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ en_uvec: 'False'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [800, 624.0]
+ rotation: 0
+ state: true
+- name: mult
+ id: blocks_multiply_xx
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ num_inputs: '2'
+ type: complex
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [960, 88.0]
+ rotation: 0
+ state: true
+- name: note
+ id: note
+ parameters:
+ alias: ''
+ comment: 'This flowgraph will generate bursty upchirps and will convert them
+
+ to PDUs by tagging them based on amplitude and converting using
+
+ the Tags to PDU block.'
+ note: README
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [40, 644.0]
+ rotation: 0
+ state: true
+- name: qtgui_time_sink_x_0
+ id: qtgui_time_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'
+ autoscale: 'False'
+ axislabels: 'True'
+ color1: blue
+ color10: dark blue
+ color2: red
+ color3: green
+ color4: black
+ color5: cyan
+ color6: magenta
+ color7: yellow
+ color8: dark red
+ color9: dark green
+ comment: ''
+ ctrlpanel: 'False'
+ entags: 'True'
+ grid: 'False'
+ gui_hint: ''
+ label1: Signal 1
+ label10: Signal 10
+ label2: Signal 2
+ label3: Signal 3
+ label4: Signal 4
+ label5: Signal 5
+ label6: Signal 6
+ label7: Signal 7
+ label8: Signal 8
+ label9: Signal 9
+ legend: 'True'
+ marker1: '-1'
+ marker10: '-1'
+ marker2: '-1'
+ marker3: '-1'
+ marker4: '-1'
+ marker5: '-1'
+ marker6: '-1'
+ marker7: '-1'
+ marker8: '-1'
+ marker9: '-1'
+ name: '""'
+ nconnections: '2'
+ size: '102400'
+ srate: samp_rate
+ stemplot: 'False'
+ style1: '1'
+ style10: '1'
+ style2: '1'
+ style3: '1'
+ style4: '1'
+ style5: '1'
+ style6: '1'
+ style7: '1'
+ style8: '1'
+ style9: '1'
+ tr_chan: '0'
+ tr_delay: '0'
+ tr_level: '0.0'
+ tr_mode: qtgui.TRIG_MODE_FREE
+ tr_slope: qtgui.TRIG_SLOPE_POS
+ tr_tag: '""'
+ type: float
+ update_time: '0.10'
+ width1: '1'
+ width10: '1'
+ width2: '1'
+ width3: '1'
+ width4: '1'
+ width5: '1'
+ width6: '1'
+ width7: '1'
+ width8: '1'
+ width9: '1'
+ ylabel: Amplitude
+ ymax: '1'
+ ymin: '-1'
+ yunit: '""'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [464, 476.0]
+ rotation: 0
+ state: true
+- name: qtgui_waterfall_sink_x_0
+ id: qtgui_waterfall_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'
+ bw: samp_rate
+ color1: '0'
+ color10: '0'
+ color2: '0'
+ color3: '0'
+ color4: '0'
+ color5: '0'
+ color6: '0'
+ color7: '0'
+ color8: '0'
+ color9: '0'
+ comment: ''
+ fc: '0'
+ fftsize: '128'
+ freqhalf: 'True'
+ grid: 'False'
+ gui_hint: 0,1,1,1
+ int_max: '10'
+ int_min: '-140'
+ label1: ''
+ label10: ''
+ label2: ''
+ label3: ''
+ label4: ''
+ label5: ''
+ label6: ''
+ label7: ''
+ label8: ''
+ label9: ''
+ legend: 'True'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ name: '""'
+ nconnections: '1'
+ showports: 'False'
+ type: complex
+ update_time: '0.002'
+ wintype: window.WIN_BLACKMAN_hARRIS
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1216, 628.0]
+ rotation: 0
+ state: true
+- name: rand
+ id: analog_random_source_x
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ max: '2'
+ maxoutbuf: '0'
+ min: '0'
+ minoutbuf: '0'
+ num_samps: '10000'
+ repeat: 'True'
+ type: short
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [32, 172.0]
+ rotation: 0
+ state: true
+- name: rep
+ id: blocks_repeat
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ interp: '10240'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ type: short
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [248, 196.0]
+ rotation: 0
+ state: true
+- name: s2f
+ id: blocks_short_to_float
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ scale: '1'
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [472, 196.0]
+ rotation: 0
+ state: true
+- name: sig
+ id: analog_sig_source_x
+ parameters:
+ affinity: ''
+ alias: ''
+ amp: '1'
+ comment: ''
+ freq: '10'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ offset: '-0.5'
+ phase: '0'
+ samp_rate: samp_rate
+ type: float
+ waveform: analog.GR_TRI_WAVE
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [336, 36.0]
+ rotation: 0
+ state: true
+- name: tagger
+ id: blocks_burst_tagger
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ false_key: EOB
+ false_value: 'False'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ true_key: SOB
+ true_value: 'True'
+ type: complex
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1088, 316.0]
+ rotation: 0
+ state: true
+- name: tags_to_pdu
+ id: pdu_tags_to_pdu_x
+ parameters:
+ affinity: ''
+ alias: ''
+ boost_time: 'True'
+ cfg_port: 'False'
+ comment: ''
+ end_tag: EOB
+ eob_alignment: '1'
+ eob_offset: '0'
+ max_pdu_size: '1024000'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ prepend: '[]'
+ pub_det: 'False'
+ rate: samp_rate
+ start_tag: SOB
+ start_time: '1.0'
+ tail_size: '0'
+ type: c
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [528, 716.0]
+ rotation: 0
+ state: enabled
+- name: thresh
+ id: blocks_threshold_ff
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ high: '0.1'
+ init: '0'
+ low: '0.08'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [672, 372.0]
+ rotation: 0
+ state: enabled
+- name: throttle
+ id: blocks_throttle
+ parameters:
+ affinity: ''
+ alias: ''
+ comment: ''
+ ignoretag: 'True'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ samples_per_second: samp_rate
+ type: float
+ vlen: '1'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [584, 76.0]
+ rotation: 0
+ state: true
+- name: time_sink
+ id: qtgui_time_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'
+ autoscale: 'False'
+ axislabels: 'True'
+ color1: blue
+ color10: dark blue
+ color2: red
+ color3: green
+ color4: black
+ color5: cyan
+ color6: magenta
+ color7: yellow
+ color8: dark red
+ color9: dark green
+ comment: ''
+ ctrlpanel: 'False'
+ entags: 'True'
+ grid: 'False'
+ gui_hint: 0,0,1,1
+ label1: Signal 1
+ label10: Signal 10
+ label2: Signal 2
+ label3: Signal 3
+ label4: Signal 4
+ label5: Signal 5
+ label6: Signal 6
+ label7: Signal 7
+ label8: Signal 8
+ label9: Signal 9
+ legend: 'True'
+ marker1: '-1'
+ marker10: '-1'
+ marker2: '-1'
+ marker3: '-1'
+ marker4: '-1'
+ marker5: '-1'
+ marker6: '-1'
+ marker7: '-1'
+ marker8: '-1'
+ marker9: '-1'
+ name: '""'
+ nconnections: '1'
+ size: '102400'
+ srate: samp_rate
+ stemplot: 'False'
+ style1: '1'
+ style10: '1'
+ style2: '1'
+ style3: '1'
+ style4: '1'
+ style5: '1'
+ style6: '1'
+ style7: '1'
+ style8: '1'
+ style9: '1'
+ tr_chan: '0'
+ tr_delay: '0'
+ tr_level: '0.0'
+ tr_mode: qtgui.TRIG_MODE_FREE
+ tr_slope: qtgui.TRIG_SLOPE_POS
+ tr_tag: '""'
+ type: complex
+ update_time: '0.10'
+ width1: '1'
+ width10: '1'
+ width2: '1'
+ width3: '1'
+ width4: '1'
+ width5: '1'
+ width6: '1'
+ width7: '1'
+ width8: '1'
+ width9: '1'
+ ylabel: Amplitude
+ ymax: '1'
+ ymin: '-1'
+ yunit: '""'
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1216, 724.0]
+ rotation: 0
+ state: true
+- name: vs0
+ id: virtual_sink
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: in
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1288, 108.0]
+ rotation: 0
+ state: true
+- name: vs1
+ id: virtual_source
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: in
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [32, 388.0]
+ rotation: 0
+ state: true
+- name: vs3
+ id: virtual_sink
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: tag
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1280, 340.0]
+ rotation: 0
+ state: true
+- name: vs4
+ id: virtual_source
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: tag
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [528, 652.0]
+ rotation: 180
+ state: true
+- name: vs4_0
+ id: virtual_source
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: tag
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [32, 508.0]
+ rotation: 0
+ state: true
+- name: vs5
+ id: virtual_source
+ parameters:
+ alias: ''
+ comment: ''
+ stream_id: in
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [1008, 644.0]
+ rotation: 0
+ state: true
+- name: waterfall
+ id: qtgui_waterfall_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'
+ bw: samp_rate
+ color1: '0'
+ color10: '0'
+ color2: '0'
+ color3: '0'
+ color4: '0'
+ color5: '0'
+ color6: '0'
+ color7: '0'
+ color8: '0'
+ color9: '0'
+ comment: ''
+ fc: '0'
+ fftsize: '1024'
+ freqhalf: 'True'
+ grid: 'False'
+ gui_hint: 0,2,1,1
+ int_max: '10'
+ int_min: '-140'
+ label1: ''
+ label10: ''
+ label2: ''
+ label3: ''
+ label4: ''
+ label5: ''
+ label6: ''
+ label7: ''
+ label8: ''
+ label9: ''
+ legend: 'True'
+ maxoutbuf: '0'
+ minoutbuf: '0'
+ name: '""'
+ nconnections: '1'
+ showports: 'False'
+ type: msg_complex
+ update_time: '0.01'
+ wintype: window.WIN_BLACKMAN_hARRIS
+ states:
+ bus_sink: false
+ bus_source: false
+ bus_structure: null
+ coordinate: [800, 732.0]
+ rotation: 0
+ state: true
+
+connections:
+- [add, '0', vs0, '0']
+- [blocks_delay_0, '0', tagger, '0']
+- [c2r, '0', qtgui_time_sink_x_0, '1']
+- [f2c, '0', mult, '1']
+- [f2s, '0', tagger, '1']
+- [fm, '0', mult, '0']
+- [fns, '0', add, '1']
+- [m2, '0', ma, '0']
+- [m2, '0', qtgui_time_sink_x_0, '0']
+- [ma, '0', thresh, '0']
+- [mult, '0', add, '0']
+- [rand, '0', rep, '0']
+- [rep, '0', s2f, '0']
+- [s2f, '0', f2c, '0']
+- [sig, '0', throttle, '0']
+- [tagger, '0', vs3, '0']
+- [tags_to_pdu, pdus, msg_dbg, print]
+- [tags_to_pdu, pdus, waterfall, in]
+- [thresh, '0', f2s, '0']
+- [throttle, '0', fm, '0']
+- [vs1, '0', blocks_delay_0, '0']
+- [vs1, '0', m2, '0']
+- [vs4, '0', tags_to_pdu, '0']
+- [vs4_0, '0', c2r, '0']
+- [vs5, '0', qtgui_waterfall_sink_x_0, '0']
+- [vs5, '0', time_sink, '0']
+
+metadata:
+ file_format: 1
diff --git a/gr-pdu/grc/pdu.tree.yml b/gr-pdu/grc/pdu.tree.yml
index 2e444e3a44..500fdaa602 100644
--- a/gr-pdu/grc/pdu.tree.yml
+++ b/gr-pdu/grc/pdu.tree.yml
@@ -10,5 +10,6 @@
- pdu_pdu_to_tagged_stream
- pdu_tagged_stream_to_pdu
- pdu_random_pdu
+ - pdu_tags_to_pdu_x
- pdu_take_skip_to_pdu
- pdu_time_delta
diff --git a/gr-pdu/grc/pdu_tags_to_pdu.block.yml b/gr-pdu/grc/pdu_tags_to_pdu.block.yml
new file mode 100644
index 0000000000..c047724027
--- /dev/null
+++ b/gr-pdu/grc/pdu_tags_to_pdu.block.yml
@@ -0,0 +1,134 @@
+id: pdu_tags_to_pdu_x
+label: Tags To PDU
+flags: [ python ]
+
+parameters:
+- id: type
+ label: PDU Type
+ dtype: enum
+ options: [c, f, i, s, b]
+ option_labels: [Complex, Float, Int, Short, Byte]
+ option_attributes:
+ input: [complex, float, int, short, byte]
+ vec_type: [complex_vector, float_vector, int_vector, int_vector, int_vector]
+ hide: part
+- id: start_tag
+ label: Start Tag
+ dtype: string
+ default: SOB
+- id: end_tag
+ label: End Tag
+ dtype: string
+ default: EOB
+- id: max_pdu_size
+ label: Max PDU Size
+ dtype: int
+ default: '1024'
+ hide: part
+- id: rate
+ label: Sample Rate
+ dtype: float
+ default: samp_rate
+ hide: part
+- id: prepend
+ label: Prepend
+ dtype: ${ type.vec_type }
+ default: '[]'
+ hide: part
+- id: cfg_port
+ label: Config Port
+ category: Optional
+ dtype: enum
+ default: 'False'
+ options: ['True', 'False']
+ option_labels: [Enabled, Disabled]
+ option_attributes:
+ hide: ['False', 'True']
+ hide: part
+- id: pub_det
+ label: Emit Detections
+ category: Optional
+ dtype: enum
+ default: 'False'
+ options: ['True', 'False']
+ option_labels: ['Yes', 'No']
+ option_attributes:
+ hide: ['False', 'True']
+ hide: part
+- id: tail_size
+ label: Tail Size
+ category: Optional
+ dtype: int
+ default: '0'
+ hide: part
+- id: eob_alignment
+ label: EOB Alignment
+ category: Optional
+ dtype: int
+ default: '1'
+ hide: part
+- id: eob_offset
+ label: EOB Offset
+ category: Optional
+ dtype: int
+ default: '0'
+ hide: part
+- id: start_time
+ label: Start Time (s)
+ category: Optional
+ dtype: float
+ default: '0.0'
+ hide: part
+- id: boost_time
+ label: Boost Time
+ category: Optional
+ dtype: enum
+ default: 'False'
+ options: ['True', 'False']
+ option_labels: ['Yes', 'No']
+ hide: part
+
+inputs:
+- domain: message
+ id: conf
+ optional: true
+ hide: ${ cfg_port.hide }
+- domain: stream
+ dtype: ${ type.input }
+
+outputs:
+- domain: message
+ id: detects
+ optional: true
+ hide: ${ pub_det.hide }
+- domain: message
+ id: pdus
+ optional: true
+
+asserts:
+- ${ max_pdu_size > 0 }
+- ${ rate > 0 }
+- ${ tail_size >= 0 }
+- ${ eob_alignment > 0 }
+- ${ eob_offset >= 0 }
+- ${ eob_offset < eob_alignment }
+- ${ start_time >= 0 }
+
+templates:
+ imports: |-
+ from gnuradio import pdu
+ import pmt
+ make: |
+ pdu.tags_to_pdu_${type}(pmt.intern(${start_tag}), pmt.intern(${end_tag}), ${max_pdu_size}, ${rate}, ${prepend}, ${pub_det}, ${tail_size}, ${start_time})
+ self.${id}.set_eob_parameters(${eob_alignment}, ${eob_offset})
+ self.${id}.enable_time_debug(${boost_time})
+ callbacks:
+ - set_start_tag(pmt.intern(${start_tag}))
+ - set_end_tag(pmt.intern(${end_tag}))
+ - set_max_pdu_size(${max_pdu_size})
+ - set_rate(${rate})
+ - set_prepend(${prepend})
+ - set_tail_size(${tail_size})
+ - set_eob_parameters(${eob_alignment}, ${eob_offset})
+
+file_format: 1
diff --git a/gr-pdu/include/gnuradio/pdu/CMakeLists.txt b/gr-pdu/include/gnuradio/pdu/CMakeLists.txt
index 5ecb3a06a1..c42d5c8522 100644
--- a/gr-pdu/include/gnuradio/pdu/CMakeLists.txt
+++ b/gr-pdu/include/gnuradio/pdu/CMakeLists.txt
@@ -18,6 +18,7 @@ install(FILES
pdu_to_stream.h
pdu_to_tagged_stream.h
random_pdu.h
+ tags_to_pdu.h
tagged_stream_to_pdu.h
take_skip_to_pdu.h
time_delta.h
diff --git a/gr-pdu/include/gnuradio/pdu/tags_to_pdu.h b/gr-pdu/include/gnuradio/pdu/tags_to_pdu.h
new file mode 100644
index 0000000000..805dedded5
--- /dev/null
+++ b/gr-pdu/include/gnuradio/pdu/tags_to_pdu.h
@@ -0,0 +1,79 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_PDU_TAGS_TO_PDU_H
+#define INCLUDED_PDU_TAGS_TO_PDU_H
+
+#include <gnuradio/pdu/api.h>
+#include <gnuradio/sync_block.h>
+
+namespace gr {
+namespace pdu {
+
+/*!
+ * \brief Tags to PDU
+ * \ingroup pdu_blk
+ *
+ * This block will generate a PDU (of up to Max PDU Size) based on input
+ * tags. No tag alignment in input buffer is necessary.
+ *
+ */
+template <class T>
+class PDU_API tags_to_pdu : virtual public gr::sync_block
+{
+public:
+ typedef std::shared_ptr<tags_to_pdu<T>> sptr;
+
+ /*!
+ * \brief Return a shared_ptr to a new instance of pdu_utils::tags_to_pdu.
+ *
+ * @param start_tag - PMT Key of the start tag
+ * @param end_tag - PMT Key of the end tag
+ * @param max_pdu_size - maximum number of items in PDU
+ * @param samp_rate - sample rate of input data stream
+ * @param prepend - data vector to prepend to the PDU
+ * @param pub_start_msg - publish message when start tag is received
+ * @param tail_size -
+ * @param start_time - time to start burst
+ */
+ static sptr make(pmt::pmt_t start_tag,
+ pmt::pmt_t end_tag,
+ uint32_t max_pdu_size,
+ double samp_rate,
+ std::vector<T> prepend,
+ bool pub_start_msg,
+ uint32_t tail_size,
+ double start_time);
+
+ virtual void set_eob_parameters(uint32_t, uint32_t) = 0;
+ virtual uint32_t get_eob_offset(void) = 0;
+ virtual uint32_t get_eob_alignment(void) = 0;
+
+ virtual void set_start_tag(pmt::pmt_t tag) = 0;
+ virtual void set_end_tag(pmt::pmt_t tag) = 0;
+ virtual void set_time_tag_key(pmt::pmt_t tag) = 0;
+ virtual void set_prepend(std::vector<T> prepend) = 0;
+ virtual void set_tail_size(uint32_t size) = 0;
+ virtual void set_max_pdu_size(uint32_t size) = 0;
+ virtual void set_samp_rate(double) = 0;
+ virtual void set_start_time(double) = 0;
+ virtual void publish_start_msgs(bool) = 0;
+ virtual void enable_time_debug(bool) = 0;
+};
+
+typedef tags_to_pdu<unsigned char> tags_to_pdu_b;
+typedef tags_to_pdu<short> tags_to_pdu_s;
+typedef tags_to_pdu<int> tags_to_pdu_i;
+typedef tags_to_pdu<float> tags_to_pdu_f;
+typedef tags_to_pdu<gr_complex> tags_to_pdu_c;
+} // namespace pdu
+} // namespace gr
+
+#endif /* INCLUDED_PDU_TAGS_TO_PDU_H */
diff --git a/gr-pdu/lib/CMakeLists.txt b/gr-pdu/lib/CMakeLists.txt
index 8bcb772ffc..606f1fcd91 100644
--- a/gr-pdu/lib/CMakeLists.txt
+++ b/gr-pdu/lib/CMakeLists.txt
@@ -17,6 +17,7 @@ add_library(gnuradio-pdu
pdu_to_stream_impl.cc
pdu_to_tagged_stream_impl.cc
random_pdu_impl.cc
+ tags_to_pdu_impl.cc
tagged_stream_to_pdu_impl.cc
take_skip_to_pdu_impl.cc
time_delta_impl.cc
diff --git a/gr-pdu/lib/tags_to_pdu_impl.cc b/gr-pdu/lib/tags_to_pdu_impl.cc
new file mode 100644
index 0000000000..a98a1771f6
--- /dev/null
+++ b/gr-pdu/lib/tags_to_pdu_impl.cc
@@ -0,0 +1,429 @@
+/* -*- 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 "tags_to_pdu_impl.h"
+#include <gnuradio/io_signature.h>
+#include <gnuradio/pdu.h>
+
+namespace gr {
+namespace pdu {
+
+template <class T>
+typename tags_to_pdu<T>::sptr tags_to_pdu<T>::make(pmt::pmt_t start_tag,
+ pmt::pmt_t end_tag,
+ uint32_t max_pdu_size,
+ double samp_rate,
+ std::vector<T> prepend,
+ bool pub_start_msg,
+ uint32_t tail_size,
+ double start_time)
+{
+ return gnuradio::make_block_sptr<tags_to_pdu_impl<T>>(start_tag,
+ end_tag,
+ max_pdu_size,
+ samp_rate,
+ prepend,
+ pub_start_msg,
+ tail_size,
+ start_time);
+}
+
+/*
+ * The private constructor
+ */
+template <class T>
+tags_to_pdu_impl<T>::tags_to_pdu_impl(pmt::pmt_t start_tag,
+ pmt::pmt_t end_tag,
+ uint32_t max_pdu_size,
+ double samp_rate,
+ std::vector<T> prepend,
+ bool pub_start_msg,
+ uint32_t tail_size,
+ double start_time)
+ : gr::sync_block("tags_to_pdu",
+ gr::io_signature::make(1, 1, sizeof(T)),
+ gr::io_signature::make(0, 0, 0)),
+ d_sob_tag_key(start_tag),
+ d_eob_tag_key(end_tag),
+ d_time_tag_key(metadata_keys::rx_time()),
+ d_max_pdu_size(max_pdu_size),
+ d_samp_rate(samp_rate),
+ d_prepend(prepend),
+ d_pub_start_msg(pub_start_msg),
+ d_tail_size(tail_size),
+ d_triggered(false),
+ d_burst_counter(0),
+ d_sob_tag_offset(0),
+ d_meta_dict(pmt::make_dict()),
+ d_wall_clock_time(false)
+{
+ // start times that will have roundoff issues are outside the intentions of this
+ // parameter
+ set_start_time(start_time);
+ set_eob_parameters(1, 0);
+ GR_LOG_NOTICE(this->d_logger,
+ boost::format("starting at time {%d %f}") % d_known_time_int_sec %
+ d_known_time_frac_sec);
+
+ GR_LOG_NOTICE(this->d_logger, boost::format("rate %0.12f") % d_samp_rate);
+
+ this->message_port_register_in(msgport_names::conf());
+ this->set_msg_handler(msgport_names::conf(),
+ [this](pmt::pmt_t msg) { this->handle_ctrl_msg(msg); });
+ this->message_port_register_out(msgport_names::pdus());
+ this->message_port_register_out(msgport_names::detects());
+}
+
+/*
+ * Our virtual destructor.
+ */
+template <class T>
+tags_to_pdu_impl<T>::~tags_to_pdu_impl()
+{
+}
+
+template <class T>
+void tags_to_pdu_impl<T>::handle_ctrl_msg(pmt::pmt_t ctrl_msg)
+{
+ // if we do not receive a dictionary, don't do anything:
+ if (!pmt::is_dict(ctrl_msg))
+ return;
+
+ // check dict for EOB offset command
+ if (pmt::dict_has_key(ctrl_msg, eob_offset())) {
+ uint32_t new_eob_offset = pmt::to_uint64(
+ pmt::dict_ref(ctrl_msg, eob_offset(), pmt::from_uint64(d_eob_offset)));
+ set_eob_parameters(d_eob_alignment, new_eob_offset);
+ GR_LOG_NOTICE(
+ this->d_logger,
+ boost::format("command received - set EOB tag offset to %d symbols") %
+ d_eob_offset);
+ }
+
+ // check dict for EOB alignment command
+ if (pmt::dict_has_key(ctrl_msg, eob_alignment())) {
+ uint32_t new_eob_alignment = pmt::to_uint64(
+ pmt::dict_ref(ctrl_msg, eob_alignment(), pmt::from_uint64(d_eob_alignment)));
+ if (new_eob_alignment > 0) {
+ set_eob_parameters(new_eob_alignment, d_eob_offset);
+ GR_LOG_NOTICE(
+ this->d_logger,
+ boost::format("command received - set EOB tag alignment to %d symbols") %
+ d_eob_alignment);
+ } else {
+ GR_LOG_ERROR(this->d_logger,
+ boost::format("command received - illegal value %d for EOB "
+ "alignment, not setting") %
+ new_eob_alignment);
+ }
+ }
+
+ // handle other control messages...
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::publish_message()
+{
+ /* determine the time. we always have the offset that the SOB tag
+ * was received on, we know the rate, and also have a known time/offset
+ * pair - calculate the delta and apply accordingly.
+ */
+ double delta;
+ delta = ((int64_t)d_sob_tag_offset - (int64_t)d_known_time_offset) / d_samp_rate;
+ int int_delta = (int)delta;
+ delta -= int_delta;
+
+ uint64_t int_seconds = d_known_time_int_sec + int_delta;
+ double frac_seconds = d_known_time_frac_sec + delta;
+
+ // keep fractional seconds in range [0, 1.0)
+ if (frac_seconds >= 1.0) {
+ frac_seconds -= 1.0;
+ int_seconds += 1;
+ }
+ pmt::pmt_t time_tuple =
+ pmt::make_tuple(pmt::from_uint64(int_seconds), pmt::from_double(frac_seconds));
+ d_meta_dict = pmt::dict_add(d_meta_dict, metadata_keys::rx_time(), time_tuple);
+ d_meta_dict = pmt::dict_add(
+ d_meta_dict, metadata_keys::pdu_num(), pmt::from_uint64(d_burst_counter));
+ if (d_wall_clock_time) {
+ double t_now((boost::get_system_time() - d_epoch).total_microseconds() /
+ 1000000.0);
+ d_meta_dict = pmt::dict_add(
+ d_meta_dict, metadata_keys::sys_time(), pmt::from_double(t_now));
+ }
+ if (d_vector.size() > d_tail_size)
+ this->message_port_pub(msgport_names::pdus(),
+ pmt::cons(d_meta_dict, init_data(d_vector)));
+
+ // prepare for next burst
+ d_burst_counter++;
+ d_triggered = false;
+ d_vector.clear();
+}
+
+template <class T>
+int tags_to_pdu_impl<T>::work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items)
+{
+ const T* in = (const T*)input_items[0];
+ uint32_t consumed = noutput_items;
+
+ uint64_t a_start = this->nitems_read(0);
+ uint64_t a_end = a_start + noutput_items;
+
+ // get all tags:
+ this->get_tags_in_range(d_tags, 0, a_start, a_end);
+
+ // find first SOB/EOB tag and process an time/offset tags encountered
+ d_tag_type = NONE;
+ for (size_t ii = 0; ii < d_tags.size(); ii++) {
+ d_tag = d_tags[ii];
+ if (pmt::eqv(d_tag.key, d_sob_tag_key)) {
+ d_tag_type = SOB;
+ consumed = d_tag.offset - a_start + 1;
+ break;
+ } else if (pmt::eqv(d_tag.key, d_eob_tag_key)) {
+ d_tag_type = EOB;
+ consumed = d_tag.offset - a_start + 1;
+ break;
+ } else if (pmt::eqv(d_tag.key, d_time_tag_key)) {
+ set_known_time_offset(pmt::to_uint64(pmt::tuple_ref(d_tag.value, 0)),
+ pmt::to_double(pmt::tuple_ref(d_tag.value, 1)),
+ d_tag.offset);
+ }
+ }
+
+ /* if the system is already triggered (has received SOB), we will be storing data
+ * until we reach an EOB tag or the maximum PDU size is reached - regardless of what
+ * happens we will consume data up to and including the received tag
+ */
+ if (d_triggered) {
+
+ uint64_t last_addr_in_vec = (d_max_pdu_size - d_vector.size()) + a_start - 1;
+
+ /* if we got an EOB/SOB tag, and the tag offset is before the max pdu
+ * length, we need to take action on it
+ */
+ if ((d_tag_type != NONE) && (d_tag.offset <= last_addr_in_vec)) {
+
+ if (d_tag_type == EOB) {
+
+ // for EOB, always append data up to (not including) tagged sample
+ d_vector.insert(d_vector.end(), &in[0], &in[consumed - 1]);
+
+ // check to see if the EOB tag is correctly aligned within the burst
+ size_t n_aligned_needed =
+ (d_vector.size() - d_eob_offset) % d_eob_alignment;
+ if (n_aligned_needed == 0) {
+ publish_message();
+
+ } else {
+ // if misaligned, publish immediately and don't worry about it
+ for (size_t i = 0; i < (d_eob_alignment - n_aligned_needed); i++) {
+ d_vector.push_back(0);
+ }
+ publish_message();
+ }
+
+ // if we have received a second SOB tag, reset and dump previous data
+ } else if (d_tag_type == SOB) {
+ GR_LOG_ERROR(this->d_logger,
+ boost::format("SOB tag received during burst %d at offset "
+ "%d, previous burst dropped (%d tags total)") %
+ d_burst_counter % d_tag.offset % d_tags.size());
+
+ // prepare for next burst
+ d_burst_counter++;
+ d_triggered = false;
+ d_vector.clear();
+
+ // do not consume SOB item so it can be handled next work() call
+ consumed--;
+ }
+
+ // otherwise, consume data and evaluate if we need to publish a message
+ } else {
+ // don't consume the sample with the SOB tag...
+ consumed--;
+ d_vector.insert(d_vector.end(), &in[0], &in[consumed]);
+ if (d_vector.size() >= d_max_pdu_size) {
+ d_vector.resize(d_max_pdu_size);
+ publish_message();
+ }
+ }
+
+ /* if we are not triggered, we are waiting for an SOB tag, until that is
+ * reached, save no data or do anything other than consume samples up to
+ * and including the observed tag other than warn if EOB tags are seen
+ */
+ } else {
+ if (d_tag_type == SOB) {
+ // the sob tag offset will be used to determine received time
+ d_sob_tag_offset = d_tag.offset;
+
+ // store the prepended data
+ d_vector.insert(d_vector.end(), d_prepend.begin(), d_prepend.end());
+ d_vector.push_back(in[consumed - 1]); // store and consume the tagged sample
+ d_triggered = true;
+
+ if (d_pub_start_msg) {
+ this->message_port_pub(msgport_names::detects(),
+ pmt::from_uint64(d_sob_tag_offset));
+ }
+
+ } else if (d_tag_type == EOB) {
+ // receiving an EOB sequence while not triggered is just random chance. No
+ // warning necessary... log at INFO level
+ GR_LOG_INFO(this->d_logger,
+ boost::format("received unexpected EOB at offset %d") %
+ d_tag.offset);
+
+ } else {
+ // do nothing
+ }
+ }
+
+ return consumed;
+}
+
+template <class T>
+void tags_to_pdu_impl<T>::set_start_tag(pmt::pmt_t tag)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_sob_tag_key = tag;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_end_tag(pmt::pmt_t tag)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_eob_tag_key = tag;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_time_tag_key(pmt::pmt_t tag)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_time_tag_key = tag;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_start_time(double start_time)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ uint64_t sec = (uint64_t)start_time;
+ double frac = (start_time - sec);
+ set_known_time_offset(sec, frac, 0);
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_known_time_offset(uint64_t int_sec,
+ double frac_sec,
+ uint64_t offset)
+{
+ d_known_time_int_sec = int_sec;
+ d_known_time_frac_sec = frac_sec;
+ d_known_time_offset = offset;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_eob_parameters(uint32_t alignment, uint32_t offset)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_eob_alignment = alignment;
+ d_eob_offset = offset;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_prepend(std::vector<T> prepend)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_prepend = prepend;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_samp_rate(double rate)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_samp_rate = rate;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_tail_size(uint32_t size)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_tail_size = size;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::set_max_pdu_size(uint32_t size)
+{
+ gr::thread::scoped_lock l(this->d_setlock);
+
+ d_max_pdu_size = size;
+}
+
+
+template <class T>
+void tags_to_pdu_impl<T>::enable_time_debug(bool enable)
+{
+ if (enable) {
+ boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
+ d_epoch = epoch;
+ d_wall_clock_time = true;
+ } else {
+ d_wall_clock_time = false;
+ }
+}
+
+template <class T>
+const pmt::pmt_t tags_to_pdu_impl<T>::eob_alignment()
+{
+ static const pmt::pmt_t val = pmt::mp("eob_alignment");
+ return val;
+}
+template <class T>
+const pmt::pmt_t tags_to_pdu_impl<T>::eob_offset()
+{
+ static const pmt::pmt_t val = pmt::mp("eob_offset");
+ return val;
+}
+
+template class tags_to_pdu<unsigned char>;
+template class tags_to_pdu<short>;
+template class tags_to_pdu<int>;
+template class tags_to_pdu<float>;
+template class tags_to_pdu<gr_complex>;
+} /* namespace pdu */
+} /* namespace gr */
diff --git a/gr-pdu/lib/tags_to_pdu_impl.h b/gr-pdu/lib/tags_to_pdu_impl.h
new file mode 100644
index 0000000000..e87c84f3b5
--- /dev/null
+++ b/gr-pdu/lib/tags_to_pdu_impl.h
@@ -0,0 +1,115 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2021 NTESS LLC.
+ *
+ * This file is part of GNU Radio
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ */
+
+#ifndef INCLUDED_PDU_TAGS_TO_PDU_IMPL_H
+#define INCLUDED_PDU_TAGS_TO_PDU_IMPL_H
+
+#include <gnuradio/pdu/tags_to_pdu.h>
+
+namespace gr {
+namespace pdu {
+
+template <class T>
+class tags_to_pdu_impl : public tags_to_pdu<T>
+{
+private:
+ pmt::pmt_t d_sob_tag_key;
+ pmt::pmt_t d_eob_tag_key;
+ pmt::pmt_t d_time_tag_key;
+ uint32_t d_max_pdu_size;
+ double d_samp_rate;
+ std::vector<T> d_prepend;
+ bool d_pub_start_msg;
+ uint32_t d_tail_size;
+ bool d_triggered;
+ uint64_t d_burst_counter;
+ uint64_t d_sob_tag_offset;
+ pmt::pmt_t d_meta_dict;
+ bool d_wall_clock_time;
+
+ std::vector<T> d_vector;
+ std::vector<tag_t> d_tags;
+ tag_t d_tag;
+ boost::posix_time::ptime d_epoch;
+ uint64_t d_known_time_int_sec; // known integer seconds of a particular item
+ double d_known_time_frac_sec; // known fractional seconds of a particular item
+ uint64_t d_known_time_offset; // known item offset of a particular item
+ uint32_t d_eob_alignment;
+ uint32_t d_eob_offset;
+
+ enum TAG_TYPE { NONE = 0, SOB, EOB } d_tag_type;
+
+ void publish_message(void);
+ void set_known_time_offset(uint64_t, double, uint64_t);
+ void handle_ctrl_msg(pmt::pmt_t ctrl_msg);
+
+ // overloaded PMT uniform vector initializers
+ inline pmt::pmt_t init_data(std::vector<unsigned char> data)
+ {
+ return pmt::init_u8vector(data.size(), (const unsigned char*)&data[0]);
+ }
+ inline pmt::pmt_t init_data(std::vector<short> data)
+ {
+ return pmt::init_s16vector(data.size(), (const short*)&data[0]);
+ }
+ inline pmt::pmt_t init_data(std::vector<int> data)
+ {
+ return pmt::init_s32vector(data.size(), (const int*)&data[0]);
+ }
+ inline pmt::pmt_t init_data(std::vector<float> data)
+ {
+ return pmt::init_f32vector(data.size(), (const float*)&data[0]);
+ }
+ inline pmt::pmt_t init_data(std::vector<gr_complex> data)
+ {
+ return pmt::init_c32vector(data.size(), (const gr_complex*)&data[0]);
+ }
+
+ // PMT constant string getters
+ const pmt::pmt_t eob_alignment();
+ const pmt::pmt_t eob_offset();
+
+public:
+ tags_to_pdu_impl(pmt::pmt_t start_tag,
+ pmt::pmt_t end_tag,
+ uint32_t max_pdu_size,
+ double samp_rate,
+ std::vector<T> prepend,
+ bool pub_start_msg,
+ uint32_t tail_size,
+ double start_time);
+
+ ~tags_to_pdu_impl() override;
+
+ // public callbacks
+ void set_start_tag(pmt::pmt_t) override;
+ void set_end_tag(pmt::pmt_t) override;
+ void set_time_tag_key(pmt::pmt_t) override;
+ void set_samp_rate(double) override;
+ void set_start_time(double) override;
+ void set_prepend(std::vector<T>) override;
+ void set_tail_size(uint32_t) override;
+ void set_max_pdu_size(uint32_t) override;
+ void publish_start_msgs(bool pub) override { d_pub_start_msg = pub; };
+ void set_eob_parameters(uint32_t, uint32_t) override;
+ void enable_time_debug(bool) override;
+ uint32_t get_eob_offset() override { return d_eob_offset; };
+ uint32_t get_eob_alignment() override { return d_eob_alignment; };
+
+ // Where all the action really happens
+ int work(int noutput_items,
+ gr_vector_const_void_star& input_items,
+ gr_vector_void_star& output_items) override;
+};
+
+} // namespace pdu
+} // namespace gr
+
+#endif /* INCLUDED_PDU_TAGS_TO_PDU_IMPL_H */
diff --git a/gr-pdu/python/pdu/bindings/CMakeLists.txt b/gr-pdu/python/pdu/bindings/CMakeLists.txt
index 832f6a84b3..a9501b83de 100644
--- a/gr-pdu/python/pdu/bindings/CMakeLists.txt
+++ b/gr-pdu/python/pdu/bindings/CMakeLists.txt
@@ -13,6 +13,7 @@ list(APPEND pdu_python_files
pdu_to_stream_python.cc
pdu_to_tagged_stream_python.cc
random_pdu_python.cc
+ tags_to_pdu_python.cc
tagged_stream_to_pdu_python.cc
take_skip_to_pdu_python.cc
time_delta_python.cc
diff --git a/gr-pdu/python/pdu/bindings/docstrings/tags_to_pdu_pydoc_template.h b/gr-pdu/python/pdu/bindings/docstrings/tags_to_pdu_pydoc_template.h
new file mode 100644
index 0000000000..b0991ef41a
--- /dev/null
+++ b/gr-pdu/python/pdu/bindings/docstrings/tags_to_pdu_pydoc_template.h
@@ -0,0 +1,15 @@
+/*
+ * 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_utils, __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
+ */
diff --git a/gr-pdu/python/pdu/bindings/python_bindings.cc b/gr-pdu/python/pdu/bindings/python_bindings.cc
index 5adc694b66..b5ed80eecb 100644
--- a/gr-pdu/python/pdu/bindings/python_bindings.cc
+++ b/gr-pdu/python/pdu/bindings/python_bindings.cc
@@ -23,6 +23,7 @@ void bind_pdu_split(py::module&);
void bind_pdu_to_stream(py::module&);
void bind_pdu_to_tagged_stream(py::module&);
void bind_random_pdu(py::module&);
+void bind_tags_to_pdu(py::module&);
void bind_tagged_stream_to_pdu(py::module&);
void bind_take_skip_to_pdu(py::module&);
void bind_time_delta(py::module&);
@@ -54,6 +55,7 @@ PYBIND11_MODULE(pdu_python, m)
bind_pdu_to_stream(m);
bind_pdu_to_tagged_stream(m);
bind_random_pdu(m);
+ bind_tags_to_pdu(m);
bind_tagged_stream_to_pdu(m);
bind_take_skip_to_pdu(m);
bind_time_delta(m);
diff --git a/gr-pdu/python/pdu/bindings/tags_to_pdu_python.cc b/gr-pdu/python/pdu/bindings/tags_to_pdu_python.cc
new file mode 100644
index 0000000000..d3c414e7ac
--- /dev/null
+++ b/gr-pdu/python/pdu/bindings/tags_to_pdu_python.cc
@@ -0,0 +1,75 @@
+/*
+ * 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(tags_to_pdu.h) */
+/* BINDTOOL_HEADER_FILE_HASH(f8652dc9d3f3d190495413fc69e5a738) */
+/***********************************************************************************/
+
+#include <pybind11/complex.h>
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+
+namespace py = pybind11;
+
+#include <gnuradio/pdu/tags_to_pdu.h>
+// pydoc.h is automatically generated in the build directory
+#include <tags_to_pdu_pydoc.h>
+
+template <typename T>
+void bind_tags_to_pdu_template(py::module& m, const char* classname)
+{
+ using tags_to_pdu = ::gr::pdu::tags_to_pdu<T>;
+
+ py::class_<tags_to_pdu,
+ gr::sync_block,
+ gr::block,
+ gr::basic_block,
+ std::shared_ptr<tags_to_pdu>>(m, classname)
+ .def(py::init(&gr::pdu::tags_to_pdu<T>::make),
+ py::arg("start_tag"),
+ py::arg("end_tag"),
+ py::arg("max_pdu_size"),
+ py::arg("samp_rate"),
+ py::arg("prepend"),
+ py::arg("pub_sobs"),
+ py::arg("tail_size"),
+ py::arg("start_time"))
+ .def("set_eob_parameters",
+ &tags_to_pdu::set_eob_parameters,
+ py::arg("alignment"),
+ py::arg("offset"))
+ .def("get_eob_offset", &tags_to_pdu::get_eob_offset)
+ .def("get_eob_alignment", &tags_to_pdu::get_eob_alignment)
+ .def("set_start_tag", &tags_to_pdu::set_start_tag, py::arg("tag"))
+ .def("set_end_tag", &tags_to_pdu::set_end_tag, py::arg("tag"))
+ .def("set_time_tag_key", &tags_to_pdu::set_time_tag_key, py::arg("tag"))
+ .def("set_prepend", &tags_to_pdu::set_prepend, py::arg("prepend"))
+ .def("set_tail_size", &tags_to_pdu::set_tail_size, py::arg("size"))
+ .def("set_max_pdu_size", &tags_to_pdu::set_max_pdu_size, py::arg("size"))
+ .def("set_samp_rate", &tags_to_pdu::set_samp_rate, py::arg("rate"))
+ .def("set_start_time", &tags_to_pdu::set_start_time, py::arg("start_time"))
+ .def("publish_start_msgs", &tags_to_pdu::publish_start_msgs, py::arg("pub"))
+ .def("enable_time_debug", &tags_to_pdu::enable_time_debug, py::arg("enable"));
+}
+
+void bind_tags_to_pdu(py::module& m)
+{
+
+ bind_tags_to_pdu_template<unsigned char>(m, "tags_to_pdu_b");
+ bind_tags_to_pdu_template<short>(m, "tags_to_pdu_s");
+ bind_tags_to_pdu_template<int>(m, "tags_to_pdu_i");
+ bind_tags_to_pdu_template<float>(m, "tags_to_pdu_f");
+ bind_tags_to_pdu_template<gr_complex>(m, "tags_to_pdu_c");
+}
diff --git a/gr-pdu/python/pdu/qa_tags_to_pdu.py b/gr-pdu/python/pdu/qa_tags_to_pdu.py
new file mode 100755
index 0000000000..de12af4f20
--- /dev/null
+++ b/gr-pdu/python/pdu/qa_tags_to_pdu.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2018-2021 National Technology & Engineering Solutions of Sandia, LLC
+# (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government
+# retains certain rights in this software.
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
+from gnuradio import gr, gr_unittest, blocks, pdu
+import numpy as np
+import pmt
+import time
+
+class qa_tags_to_pdu (gr_unittest.TestCase):
+
+ def setUp (self):
+ pass
+
+ def tearDown (self):
+ pass
+
+ def test_001_simple (self):
+ self.tb = gr.top_block ()
+ start_time = 0.1
+ sob_tag = gr.tag_utils.python_to_tag((34, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag = gr.tag_utils.python_to_tag((34+(8*31), pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ vs = blocks.vector_source_s(range(350), False, 1, [sob_tag, eob_tag])
+ t2p = pdu.tags_to_pdu_s(pmt.intern('SOB'), pmt.intern('EOB'), 1024, 512000, ([]), False, 0, start_time)
+ t2p.set_eob_parameters(8, 0)
+ dbg = blocks.message_debug()
+ self.tb.connect(vs, t2p)
+ self.tb.msg_connect((t2p, 'pdus'), (dbg, 'store'))
+ expected_vec = pmt.init_s16vector((8*31), range(34,34+(8*31)))
+ expected_time = start_time + (34 / 512000.0)
+
+ self.tb.run ()
+
+ self.assertEqual(dbg.num_messages(), 1)
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(0)), expected_vec))
+ time_tuple1 = pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple1,0)) + pmt.to_double(pmt.tuple_ref(time_tuple1,1)), expected_time)
+
+ self.tb = None
+
+
+ def test_002_secondSOB (self):
+ self.tb = gr.top_block ()
+ start_time = 4.999999999
+ sob_tag = gr.tag_utils.python_to_tag((34, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ sob_tag2 = gr.tag_utils.python_to_tag((51, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag = gr.tag_utils.python_to_tag((51+(8*26), pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ vs = blocks.vector_source_s(range(350), False, 1, [sob_tag, sob_tag2, eob_tag])
+ t2p = pdu.tags_to_pdu_s(pmt.intern('SOB'), pmt.intern('EOB'), 1024, 460800, ([]), False, 0, start_time)
+ t2p.set_eob_parameters(8, 0)
+ dbg = blocks.message_debug()
+ self.tb.connect(vs, t2p)
+ self.tb.msg_connect((t2p, 'pdus'), (dbg, 'store'))
+ expected_vec = pmt.init_s16vector((8*26), range(51,51+(8*26)))
+ expected_time = start_time + (51 / 460800.0)
+
+ self.tb.run ()
+
+ self.assertEqual(dbg.num_messages(), 1)
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(0)), expected_vec))
+ time_tuple1 = pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple1,0)) + pmt.to_double(pmt.tuple_ref(time_tuple1,1)), expected_time)
+
+ self.tb = None
+
+
+ def test_003_double_eob_rej_tt_update (self):
+ self.tb = gr.top_block ()
+ start_time = 0.0
+ sob_tag = gr.tag_utils.python_to_tag((51, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag = gr.tag_utils.python_to_tag((51+(8*11), pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ time_tuple = pmt.make_tuple(pmt.from_uint64(4), pmt.from_double(0.125), pmt.from_uint64(10000000), pmt.from_double(4000000.0))
+ time_tag = gr.tag_utils.python_to_tag((360, pmt.intern("rx_time"), time_tuple, pmt.intern("src")))
+ sob_tag2 = gr.tag_utils.python_to_tag((400, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag2e = gr.tag_utils.python_to_tag((409, pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag2 = gr.tag_utils.python_to_tag((416, pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ vs = blocks.vector_source_s(range(500), False, 1, [sob_tag, eob_tag, time_tag, sob_tag2, eob_tag2e, eob_tag2])
+ t2p = pdu.tags_to_pdu_s(pmt.intern('SOB'), pmt.intern('EOB'), 1024, 1000000, ([]), False, 0, start_time)
+ t2p.set_eob_parameters(8, 0)
+ dbg = blocks.message_debug()
+ self.tb.connect(vs, t2p)
+ self.tb.msg_connect((t2p, 'pdus'), (dbg, 'store'))
+ expected_vec1 = pmt.init_s16vector((8*11), range(51,51+(8*11)))
+ expected_vec2 = pmt.init_s16vector(16, list(range(400,409)) + [0]*7)
+ expected_time1 = start_time + (51 / 1000000.0)
+ expected_time2 = 4.125 + ((400-360) / 1000000.0)
+
+ self.tb.run ()
+
+ self.assertEqual(dbg.num_messages(), 2)
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(0)), expected_vec1))
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(1)), expected_vec2))
+ time_tuple1 = pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ time_tuple2 = pmt.dict_ref(pmt.car(dbg.get_message(1)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple1,0)) + pmt.to_double(pmt.tuple_ref(time_tuple1,1)), expected_time1)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple2,0)) + pmt.to_double(pmt.tuple_ref(time_tuple2,1)), expected_time2)
+
+ self.tb = None
+
+ def test_004_boost_time (self):
+ self.tb = gr.top_block ()
+ start_time = 0.1
+ sob_tag = gr.tag_utils.python_to_tag((34, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag = gr.tag_utils.python_to_tag((34+(8*31), pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ vs = blocks.vector_source_s(range(350), False, 1, [sob_tag, eob_tag])
+ t2p = pdu.tags_to_pdu_s(pmt.intern('SOB'), pmt.intern('EOB'), 1024, 512000, ([]), False, 0, start_time)
+ t2p.enable_time_debug(True)
+ t2p.set_eob_parameters(8, 0)
+ dbg = blocks.message_debug()
+
+ self.tb.connect(vs, t2p)
+ self.tb.msg_connect((t2p, 'pdus'), (dbg, 'store'))
+ expected_vec = pmt.init_s16vector((8*31), range(34,34+(8*31)))
+ expected_time = start_time + (34 / 512000.0)
+ ts = time.time()
+ self.tb.run ()
+
+ self.assertEqual(dbg.num_messages(), 1)
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(0)), expected_vec))
+ time_tuple1 = pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple1,0)) + pmt.to_double(pmt.tuple_ref(time_tuple1,1)), expected_time)
+ #wct = pmt.to_double(pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("wall_clock_time"), pmt.PMT_NIL))
+ #self.assertTrue((wct - ts) < 1.0)
+
+ self.tb = None
+
+ def test_005_two_sobs_misaligned (self):
+ # Two SOB tags and the SOB-to-EOB length is not aligned
+ self.tb = gr.top_block ()
+ start_time = 0.1
+ sob_tag = gr.tag_utils.python_to_tag((34, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ sob_tag2 = gr.tag_utils.python_to_tag((35, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag = gr.tag_utils.python_to_tag((34+(8*31), pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ vs = blocks.vector_source_s(range(1350), False, 1, [sob_tag, sob_tag2, eob_tag])
+ t2p = pdu.tags_to_pdu_s(pmt.intern('SOB'), pmt.intern('EOB'), 1024, 512000, ([]), False, 0, start_time)
+ t2p.set_eob_parameters(8, 0)
+ dbg = blocks.message_debug()
+ self.tb.connect(vs, t2p)
+ self.tb.msg_connect((t2p, 'pdus'), (dbg, 'store'))
+ expected_vec = pmt.init_s16vector((8*31), list(range(35,34+(8*31))) + [0])
+ expected_time = start_time + (35 / 512000.0)
+
+ self.tb.run ()
+
+ self.assertEqual(dbg.num_messages(), 1)
+ #print "got ", dbg.get_message(0)
+ #print "expected", expected_vec
+ #print "len is {}".format(len(pmt.to_python(pmt.cdr(dbg.get_message(0)))))
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(0)), expected_vec))
+ time_tuple1 = pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple1,0)) + pmt.to_double(pmt.tuple_ref(time_tuple1,1)), expected_time)
+
+ self.tb = None
+
+ def test_006_max_pdu_size (self):
+ # two SOB tags exactly max_pdu_size samples apart, with an SOB-to-EOB length that is not divisible by the alignment size
+ self.tb = gr.top_block ()
+ start_time = 0.1
+ max_size = 100
+ sob_tag = gr.tag_utils.python_to_tag((10, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ eob_tag = gr.tag_utils.python_to_tag((91, pmt.intern("EOB"), pmt.PMT_T, pmt.intern("src")))
+ sob_tag3 = gr.tag_utils.python_to_tag((11+max_size, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ vs = blocks.vector_source_s(range(1350), False, 1, [sob_tag, eob_tag, sob_tag3])
+ t2p = pdu.tags_to_pdu_s(pmt.intern('SOB'), pmt.intern('EOB'), 1024, 512000, ([]), False, 0, start_time)
+ t2p.set_eob_parameters(10, 0)
+ t2p.set_max_pdu_size(max_size)
+
+ dbg = blocks.message_debug()
+ self.tb.connect(vs, t2p)
+ self.tb.msg_connect((t2p, 'pdus'), (dbg, 'store'))
+ expected_vec = pmt.init_s16vector((9*10), list(range(10,91)) + [0]*9)
+ expected_time = start_time + (10 / 512000.0)
+
+ self.tb.run ()
+
+ # assertions for the first PDU only, second PDU will exist
+ self.assertEqual(dbg.num_messages(), 2)
+ #print "got ", dbg.get_message(0)
+ #print "expected", expected_vec
+ #print "len is {}".format(len(pmt.to_python(pmt.cdr(dbg.get_message(0)))))
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(0)), expected_vec))
+ time_tuple1 = pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple1,0)) + pmt.to_double(pmt.tuple_ref(time_tuple1,1)), expected_time)
+
+ self.tb = None
+
+ def test_007_max_pdu_size_SOBs (self):
+ # two SOB tags exactly max_pdu_size samples apart
+ self.tb = gr.top_block ()
+ start_time = 0.1
+ max_size = 100
+ sob_tag = gr.tag_utils.python_to_tag((10, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+ sob_tag3 = gr.tag_utils.python_to_tag((10+max_size, pmt.intern("SOB"), pmt.PMT_T, pmt.intern("src")))
+
+ vs = blocks.vector_source_s(range(1350), False, 1, [sob_tag, sob_tag3])
+ t2p = pdu.tags_to_pdu_s(pmt.intern('SOB'), pmt.intern('EOB'), 1024, 512000, ([]), False, 0, start_time)
+ t2p.set_eob_parameters(10, 0)
+ t2p.set_max_pdu_size(max_size)
+
+ dbg = blocks.message_debug()
+ self.tb.connect(vs, t2p)
+ self.tb.msg_connect((t2p, 'pdus'), (dbg, 'store'))
+ expected_vec = pmt.init_s16vector((max_size), range(10,10+max_size))
+ expected_time = start_time + (10 / 512000.0)
+
+ self.tb.run ()
+
+ # assertions for the first PDU only, second PDU will exist
+ self.assertEqual(dbg.num_messages(), 2)
+ #print "got ", dbg.get_message(0)
+ #print "expected", expected_vec
+ self.assertTrue(pmt.equal(pmt.cdr(dbg.get_message(0)), expected_vec))
+ time_tuple1 = pmt.dict_ref(pmt.car(dbg.get_message(0)), pmt.intern("rx_time"), pmt.PMT_NIL)
+ self.assertAlmostEqual(pmt.to_uint64(pmt.tuple_ref(time_tuple1,0)) + pmt.to_double(pmt.tuple_ref(time_tuple1,1)), expected_time)
+
+ self.tb = None
+
+
+# TODO: add more tests:
+# - test ability to correctly handle max-length pdus
+# - test callbacks
+# - test message setting interface
+# - test time-tracking tags
+
+
+if __name__ == '__main__':
+ gr_unittest.run(qa_tags_to_pdu)