diff options
-rw-r--r-- | gnuradio-runtime/include/gnuradio/pdu.h | 6 | ||||
-rw-r--r-- | gnuradio-runtime/lib/pdu.cc | 20 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/bindings/docstrings/pdu_pydoc_template.h | 12 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/bindings/pdu_python.cc | 15 | ||||
-rw-r--r-- | gr-pdu/examples/tags_to_pdu_example.grc | 840 | ||||
-rw-r--r-- | gr-pdu/grc/pdu.tree.yml | 1 | ||||
-rw-r--r-- | gr-pdu/grc/pdu_tags_to_pdu.block.yml | 134 | ||||
-rw-r--r-- | gr-pdu/include/gnuradio/pdu/CMakeLists.txt | 1 | ||||
-rw-r--r-- | gr-pdu/include/gnuradio/pdu/tags_to_pdu.h | 79 | ||||
-rw-r--r-- | gr-pdu/lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | gr-pdu/lib/tags_to_pdu_impl.cc | 429 | ||||
-rw-r--r-- | gr-pdu/lib/tags_to_pdu_impl.h | 115 | ||||
-rw-r--r-- | gr-pdu/python/pdu/bindings/CMakeLists.txt | 1 | ||||
-rw-r--r-- | gr-pdu/python/pdu/bindings/docstrings/tags_to_pdu_pydoc_template.h | 15 | ||||
-rw-r--r-- | gr-pdu/python/pdu/bindings/python_bindings.cc | 2 | ||||
-rw-r--r-- | gr-pdu/python/pdu/bindings/tags_to_pdu_python.cc | 75 | ||||
-rwxr-xr-x | gr-pdu/python/pdu/qa_tags_to_pdu.py | 233 |
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) |