diff options
33 files changed, 1910 insertions, 426 deletions
diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx index 23e11d4174..cebc00dcf4 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx +++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-perf-monitorx @@ -254,18 +254,23 @@ class DataTable(QtGui.QWidget): def update(self): print "update" + def closeEvent(self, event): + self.timer = None + def __init__(self, radioclient, G): QtGui.QWidget.__init__( self) - self.layout = QtGui.QVBoxLayout(self); - self.hlayout = QtGui.QHBoxLayout(); - self.layout.addLayout(self.hlayout); + self.layout = QtGui.QVBoxLayout(self) + self.hlayout = QtGui.QHBoxLayout() + self.layout.addLayout(self.hlayout) - self.G = G; - self.radioclient = radioclient; + self.G = G + self.radioclient = radioclient self._keymap = None + self.disp = None + # Create a combobox to set the type of statistic we want. self._statistic = "Instantaneous" self._statistics_table = {"Instantaneous": "", @@ -276,7 +281,7 @@ class DataTable(QtGui.QWidget): self.stattype.addItem("Average") self.stattype.addItem("Variance") self.stattype.setMaximumWidth(200) - self.hlayout.addWidget(self.stattype); + self.hlayout.addWidget(self.stattype) self.stattype.currentIndexChanged.connect(self.stat_changed) # Create a checkbox to toggle sorting of graphs @@ -287,18 +292,18 @@ class DataTable(QtGui.QWidget): self.checksort.stateChanged.connect(self.checksort_changed) # set up table - self.perfTable = Qt.QTableWidget(); + self.perfTable = Qt.QTableWidget() self.perfTable.setColumnCount(2) - self.perfTable.verticalHeader().hide(); - self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Runtime"] ); - self.perfTable.horizontalHeader().setStretchLastSection(True); + self.perfTable.verticalHeader().hide() + self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Runtime"] ) + self.perfTable.horizontalHeader().setStretchLastSection(True) self.perfTable.setSortingEnabled(True) nodes = self.G.nodes(data=True) # set up plot self.f = plt.figure(figsize=(10,8), dpi=90) - self.sp = self.f.add_subplot(111); - self.sp.autoscale_view(True,True,True); + self.sp = self.f.add_subplot(111) + self.sp.autoscale_view(True,True,True) self.sp.set_autoscale_on(True) self.canvas = FigureCanvas(self.f) @@ -339,63 +344,71 @@ class DataTable(QtGui.QWidget): class DataTableBuffers(DataTable): def __init__(self, radioclient, G): super(DataTableBuffers, self).__init__(radioclient, G) - self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Buffer Full"] ); + self.perfTable.setHorizontalHeaderLabels( ["Block Name", "Percent Buffer Full"] ) def update(self): nodes = self.G.nodes(); # get buffer fullness for all blocks kl = map(lambda x: "%s::%soutput %% full" % \ - (x, self._statistics_table[self._statistic]), + (x, self._statistics_table[self._statistic]), nodes); buf_knobs = self.radioclient.getKnobs(kl) # strip values out of ctrlport response buffer_fullness = dict(zip( - map(lambda x: x.split("::")[0], buf_knobs.keys()), - map(lambda x: x.value, buf_knobs.values()))) + map(lambda x: x.split("::")[0], buf_knobs.keys()), + map(lambda x: x.value, buf_knobs.values()))) blockport_fullness = {} for blk in buffer_fullness: bdata = buffer_fullness[blk] if bdata: for port in range(0,len(bdata)): - blockport_fullness["%s:%d"%(blk,port)] = bdata[port]; + blockport_fullness["%s:%d"%(blk,port)] = bdata[port] - self.table_update(blockport_fullness); + if(self.perfTable.isVisible()): + self.table_update(blockport_fullness); - if(self._sort): - sorted_fullness = sorted(blockport_fullness.iteritems(), key=operator.itemgetter(1)) - self._keymap = map(operator.itemgetter(0), sorted_fullness) else: - if self._keymap: - sorted_fullness = len(self._keymap)*['',] - for b in blockport_fullness: - sorted_fullness[self._keymap.index(b)] = (b, blockport_fullness[b]) + if(self._sort): + sorted_fullness = sorted(blockport_fullness.iteritems(), + key=operator.itemgetter(1)) + self._keymap = map(operator.itemgetter(0), sorted_fullness) + else: + if self._keymap: + sorted_fullness = len(self._keymap)*['',] + for b in blockport_fullness: + sorted_fullness[self._keymap.index(b)] = (b, blockport_fullness[b]) + else: + sorted_fullness = blockport_fullness.items() + + if(not self.disp): + self.disp = self.sp.bar(range(0,len(sorted_fullness)), + map(lambda x: x[1], sorted_fullness), + alpha=0.5) + self.sp.set_ylabel("% Buffers Full"); + self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_fullness)))) + self.sp.set_xticklabels(map(lambda x: " " + x, map(lambda x: x[0], sorted_fullness)), + rotation="vertical", verticalalignment="bottom") else: - sorted_fullness = blockport_fullness.items() + self.sp.set_xticklabels(map(lambda x: " " + x, map(lambda x: x[0], sorted_fullness)), + rotation="vertical", verticalalignment="bottom") + for r,w in zip(self.disp, sorted_fullness): + r.set_height(w[1]) - self.sp.clear(); - self.sp.bar(range(0,len(sorted_fullness)), map(lambda x: x[1], sorted_fullness), - alpha=0.5) - self.sp.set_ylabel("% Buffers Full"); - self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_fullness)))) - self.sp.set_xticklabels( map(lambda x: " " + x, map(lambda x: x[0], sorted_fullness)), - rotation="vertical", verticalalignment="bottom" ) - self.canvas.draw() - self.canvas.show() + self.canvas.draw() class DataTableRuntimes(DataTable): def __init__(self, radioclient, G): super(DataTableRuntimes, self).__init__( radioclient, G) - #self.perfTable.setRowCount(len( self.G.nodes() )) def update(self): nodes = self.G.nodes(); # get work time for all blocks kl = map(lambda x: "%s::%swork time" % \ - (x, self._statistics_table[self._statistic]), + (x, self._statistics_table[self._statistic]), nodes); wrk_knobs = self.radioclient.getKnobs(kl) @@ -408,31 +421,37 @@ class DataTableRuntimes(DataTable): map(lambda x: x.value/total_work, wrk_knobs.values()))) # update table view - self.table_update(work_times) + if(self.perfTable.isVisible()): + self.table_update(work_times) - if(self._sort): - sorted_work = sorted(work_times.iteritems(), key=operator.itemgetter(1)) - self._keymap = map(operator.itemgetter(0), sorted_work) else: - if self._keymap: - sorted_work = len(self._keymap)*['',] - for b in work_times: - sorted_work[self._keymap.index(b)] = (b, work_times[b]) + if(self._sort): + sorted_work = sorted(work_times.iteritems(), key=operator.itemgetter(1)) + self._keymap = map(operator.itemgetter(0), sorted_work) else: - sorted_work = work_times.items() - - self.sp.clear(); - plt.figure(self.f.number) - plt.subplot(111); - self.sp.bar(range(0,len(sorted_work)), map(lambda x: x[1], sorted_work), - alpha=0.5) - self.sp.set_ylabel("% Runtime"); - self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_work)))) - self.sp.set_xticklabels( map(lambda x: " " + x[0], sorted_work), - rotation="vertical", verticalalignment="bottom" ) + if self._keymap: + sorted_work = len(self._keymap)*['',] + for b in work_times: + sorted_work[self._keymap.index(b)] = (b, work_times[b]) + else: + sorted_work = work_times.items() + + f = plt.figure(self.f.number) + if(not self.disp): + self.disp = self.sp.bar(range(0,len(sorted_work)), + map(lambda x: x[1], sorted_work), + alpha=0.5) + self.sp.set_ylabel("% Runtime"); + self.sp.set_xticks( map(lambda x: x+0.5, range(0,len(sorted_work)))) + self.sp.set_xticklabels( map(lambda x: " " + x[0], sorted_work), + rotation="vertical", verticalalignment="bottom" ) + else: + self.sp.set_xticklabels( map(lambda x: " " + x[0], sorted_work), + rotation="vertical", verticalalignment="bottom" ) + for r,w in zip(self.disp, sorted_work): + r.set_height(w[1]) - self.canvas.draw(); - self.canvas.show(); + self.canvas.draw() class MForm(QtGui.QWidget): def update(self): @@ -835,10 +854,10 @@ class MForm(QtGui.QWidget): class MyApp(object): def __init__(self, args): p = gr.prefs() - cp_on = p.get_bool("ControlPort", "on", None) - cp_edges = p.get_bool("ControlPort", "edges_list", None) - pcs_on = p.get_bool("PerfCounters", "on", None) - pcs_exported = p.get_bool("PerfCounters", "export", None) + cp_on = p.get_bool("ControlPort", "on", False) + cp_edges = p.get_bool("ControlPort", "edges_list", False) + pcs_on = p.get_bool("PerfCounters", "on", False) + pcs_exported = p.get_bool("PerfCounters", "export", False) if(not (pcs_on and cp_on and pcs_exported and cp_edges)): print("Configuration has not turned on all of the appropriate ControlPort features:") print("\t[ControlPort] on = {0}".format(cp_on)) diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py index 1a08ac5ae6..55b62a12ac 100755 --- a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py @@ -76,6 +76,38 @@ class test_tag_utils (gr_unittest.TestCase): self.assertTrue(pmt.equal(t_tuple.value, value)) self.assertEqual(t_tuple.offset, offset) + def test_003(self): + offsets = (6, 3, 8) + key = pmt.string_to_symbol('key') + srcid = pmt.string_to_symbol('qa_tag_utils') + tags = [] + + for k in offsets: + t = gr.tag_t() + t.offset = k + t.key = key + t.value = pmt.from_long(k) + t.srcid = srcid + tags.append(t) + + for k, t in zip(sorted(offsets), + sorted(tags, key=gr.tag_t_offset_compare_key())): + self.assertEqual(t.offset, k) + self.assertTrue(pmt.equal(t.key, key)) + self.assertTrue(pmt.equal(t.value, pmt.from_long(k))) + self.assertTrue(pmt.equal(t.srcid, srcid)) + + tmin = min(tags, key=gr.tag_t_offset_compare_key()) + self.assertEqual(tmin.offset, min(offsets)) + self.assertTrue(pmt.equal(tmin.key, key)) + self.assertTrue(pmt.equal(tmin.value, pmt.from_long(min(offsets)))) + self.assertTrue(pmt.equal(tmin.srcid, srcid)) + + tmax = max(tags, key=gr.tag_t_offset_compare_key()) + self.assertEqual(tmax.offset, max(offsets)) + self.assertTrue(pmt.equal(tmax.key, key)) + self.assertTrue(pmt.equal(tmax.value, pmt.from_long(max(offsets)))) + self.assertTrue(pmt.equal(tmax.srcid, srcid)) if __name__ == '__main__': diff --git a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py index dc36e05250..a7745428c7 100644 --- a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py @@ -108,3 +108,36 @@ def python_to_tag(tag_struct): return tag else: return None + +def tag_t_offset_compare_key(): + """ + Convert a tag_t_offset_compare function into a key=function + This method is modeled after functools.cmp_to_key(_func_). + It can be used by functions that accept a key function, such as + sorted(), min(), max(), etc. to compare tags by their offsets, + e.g., sorted(tag_list, key=gr.tag_t_offset_compare_key()). + """ + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + # x.offset < y.offset + return gr.tag_t_offset_compare(self.obj, other.obj) + def __gt__(self, other): + # y.offset < x.offset + return gr.tag_t_offset_compare(other.obj, self.obj) + def __eq__(self, other): + # not (x.offset < y.offset) and not (y.offset < x.offset) + return not gr.tag_t_offset_compare(self.obj, other.obj) and \ + not gr.tag_t_offset_compare(other.obj, self.obj) + def __le__(self, other): + # not (y.offset < x.offset) + return not gr.tag_t_offset_compare(other.obj, self.obj) + def __ge__(self, other): + # not (x.offset < y.offset) + return not gr.tag_t_offset_compare(self.obj, other.obj) + def __ne__(self, other): + # (x.offset < y.offset) or (y.offset < x.offset) + return gr.tag_t_offset_compare(self.obj, other.obj) or \ + gr.tag_t_offset_compare(other.obj, self.obj) + return K diff --git a/gr-analog/grc/analog_pwr_squelch_xx.xml b/gr-analog/grc/analog_pwr_squelch_xx.xml index 32d9c0e947..a75f85cf10 100644 --- a/gr-analog/grc/analog_pwr_squelch_xx.xml +++ b/gr-analog/grc/analog_pwr_squelch_xx.xml @@ -34,11 +34,13 @@ <param> <name>Alpha</name> <key>alpha</key> + <value>1e-4</value> <type>real</type> </param> <param> <name>Ramp</name> <key>ramp</key> + <value>0</value> <type>int</type> </param> <param> diff --git a/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h b/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h index 6913d62934..324743d965 100644 --- a/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h +++ b/gr-analog/include/gnuradio/analog/pwr_squelch_cc.h @@ -49,10 +49,14 @@ namespace gr { * \brief Make power-based squelch block. * * \param db threshold (in dB) for power squelch - * \param alpha Gain of averaging filter - * \param ramp sets response characteristic. + * \param alpha Gain of averaging filter. Defaults to 0.0001. + * \param ramp sets response characteristic. Defaults to 0. * \param gate if true, no output if no squelch tone. - * if false, output 0's if no squelch tone. + * if false, output 0's if no squelch tone (default). + * + * The block will emit a tag with the key pmt::intern("squelch_sob") + * with the value of pmt::PMT_NIL on the first item it passes, and with + * the key pmt::intern("squelch:eob") on the last item it passes. */ static sptr make(double db, double alpha=0.0001, int ramp=0, bool gate=false); diff --git a/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h b/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h index 46046eae07..a65cedaa57 100644 --- a/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h +++ b/gr-analog/include/gnuradio/analog/pwr_squelch_ff.h @@ -49,10 +49,14 @@ namespace gr { * \brief Make power-based squelch block. * * \param db threshold (in dB) for power squelch - * \param alpha Gain of averaging filter - * \param ramp sets response characteristic. + * \param alpha Gain of averaging filter. Defaults to 0.0001. + * \param ramp sets response characteristic. Defaults to 0. * \param gate if true, no output if no squelch tone. - * if false, output 0's if no squelch tone. + * if false, output 0's if no squelch tone (default). + * + * The block will emit a tag with the key pmt::intern("squelch_sob") + * with the value of pmt::PMT_NIL on the first item it passes, and with + * the key pmt::intern("squelch:eob") on the last item it passes. */ static sptr make(double db, double alpha=0.0001, int ramp=0, bool gate=false); diff --git a/gr-analog/lib/squelch_base_cc_impl.cc b/gr-analog/lib/squelch_base_cc_impl.cc index 3255d3bde4..b5c153558b 100644 --- a/gr-analog/lib/squelch_base_cc_impl.cc +++ b/gr-analog/lib/squelch_base_cc_impl.cc @@ -32,8 +32,11 @@ namespace gr { squelch_base_cc_impl::squelch_base_cc_impl(const char *name, int ramp, bool gate) : block(name, - io_signature::make(1, 1, sizeof(float)), - io_signature::make(1, 1, sizeof(float))) + io_signature::make(1, 1, sizeof(float)), + io_signature::make(1, 1, sizeof(float))), + d_sob_key(pmt::intern("squelch_sob")), + d_eob_key(pmt::intern("squelch_eob")), + d_tag_next_unmuted(true) { set_ramp(ramp); set_gate(gate); @@ -92,48 +95,58 @@ namespace gr { gr::thread::scoped_lock l(d_setlock); for(int i = 0; i < noutput_items; i++) { - update_state(in[i]); - - // Adjust envelope based on current state - switch(d_state) { - case ST_MUTED: - if(!mute()) { - d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; // If not ramping, go straight to unmuted - } - break; - - case ST_UNMUTED: - if(mute()) { - d_state = d_ramp ? ST_DECAY : ST_MUTED; // If not ramping, go straight to muted - } - break; - - case ST_ATTACK: - d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed - if(d_ramped >= d_ramp) { // use >= in case d_ramp is set to lower value elsewhere - d_state = ST_UNMUTED; - d_envelope = 1.0; - } - break; - - case ST_DECAY: - d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed - if(d_ramped == 0.0) { - d_state = ST_MUTED; - } - break; - }; - - // If unmuted, copy input times envelope to output - // Otherwise, if not gating, copy zero to output - if(d_state != ST_MUTED) { - out[j++] = in[i]*gr_complex(d_envelope, 0.0); - } - else { - if(!d_gate) { - out[j++] = 0.0; - } - } + update_state(in[i]); + + // Adjust envelope based on current state + switch(d_state) { + case ST_MUTED: + if(!mute()) { + d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; // If not ramping, go straight to unmuted + if(d_state == ST_UNMUTED) + d_tag_next_unmuted = true; + } + break; + + case ST_UNMUTED: + if(d_tag_next_unmuted) { + d_tag_next_unmuted = false; + add_item_tag(0, nitems_written(0) + j, d_sob_key, pmt::PMT_NIL); + } + if(mute()) { + d_state = d_ramp ? ST_DECAY : ST_MUTED; // If not ramping, go straight to muted + if(d_state == ST_MUTED) + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + + case ST_ATTACK: + d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed + if(d_ramped >= d_ramp) { // use >= in case d_ramp is set to lower value elsewhere + d_state = ST_UNMUTED; + d_tag_next_unmuted = true; + d_envelope = 1.0; + } + break; + + case ST_DECAY: + d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; // FIXME: precalculate window for speed + if(d_ramped == 0.0) { + d_state = ST_MUTED; + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + }; + + // If unmuted, copy input times envelope to output + // Otherwise, if not gating, copy zero to output + if(d_state != ST_MUTED) { + out[j++] = in[i]*gr_complex(d_envelope, 0.0); + } + else { + if(!d_gate) { + out[j++] = 0.0; + } + } } consume_each(noutput_items); // Use all the inputs diff --git a/gr-analog/lib/squelch_base_cc_impl.h b/gr-analog/lib/squelch_base_cc_impl.h index 58802df91c..68ed1bb2b8 100644 --- a/gr-analog/lib/squelch_base_cc_impl.h +++ b/gr-analog/lib/squelch_base_cc_impl.h @@ -36,6 +36,8 @@ namespace gr { bool d_gate; double d_envelope; enum { ST_MUTED, ST_ATTACK, ST_UNMUTED, ST_DECAY } d_state; + const pmt::pmt_t d_sob_key, d_eob_key; + bool d_tag_next_unmuted; protected: virtual void update_state(const gr_complex &sample) {}; diff --git a/gr-analog/lib/squelch_base_ff_impl.cc b/gr-analog/lib/squelch_base_ff_impl.cc index a729fedb24..ea2d29bd97 100644 --- a/gr-analog/lib/squelch_base_ff_impl.cc +++ b/gr-analog/lib/squelch_base_ff_impl.cc @@ -26,14 +26,18 @@ #include "squelch_base_ff_impl.h" #include <gnuradio/io_signature.h> +#include <pmt/pmt.h> namespace gr { namespace analog { squelch_base_ff_impl::squelch_base_ff_impl(const char *name, int ramp, bool gate) : block(name, - io_signature::make(1, 1, sizeof(float)), - io_signature::make(1, 1, sizeof(float))) + io_signature::make(1, 1, sizeof(float)), + io_signature::make(1, 1, sizeof(float))), + d_sob_key(pmt::intern("squelch_sob")), + d_eob_key(pmt::intern("squelch_eob")), + d_tag_next_unmuted(true) { set_ramp(ramp); set_gate(gate); @@ -88,48 +92,61 @@ namespace gr { int j = 0; for(int i = 0; i < noutput_items; i++) { - update_state(in[i]); - - // Adjust envelope based on current state - switch(d_state) { - case ST_MUTED: - if(!mute()) - // If not ramping, go straight to unmuted - d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; - break; - - case ST_UNMUTED: - if(mute()) - // If not ramping, go straight to muted - d_state = d_ramp ? ST_DECAY : ST_MUTED; - break; - - case ST_ATTACK: - // FIXME: precalculate window for speed - d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; - - // use >= in case d_ramp is set to lower value elsewhere - if(d_ramped >= d_ramp) { - d_state = ST_UNMUTED; - d_envelope = 1.0; - } - break; - - case ST_DECAY: - // FIXME: precalculate window for speed - d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; - if(d_ramped == 0.0) - d_state = ST_MUTED; - break; - }; - - // If unmuted, copy input times envelope to output - // Otherwise, if not gating, copy zero to output - if(d_state != ST_MUTED) - out[j++] = in[i]*d_envelope; - else - if(!d_gate) - out[j++] = 0.0; + update_state(in[i]); + + // Adjust envelope based on current state + switch(d_state) { + case ST_MUTED: + if(!mute()) { + // If not ramping, go straight to unmuted + d_state = d_ramp ? ST_ATTACK : ST_UNMUTED; + if(d_state == ST_UNMUTED) + d_tag_next_unmuted = true; + } + break; + + case ST_UNMUTED: + if(d_tag_next_unmuted) { + d_tag_next_unmuted = false; + add_item_tag(0, nitems_written(0) + j, d_sob_key, pmt::PMT_NIL); + } + if(mute()) { + // If not ramping, go straight to muted + d_state = d_ramp ? ST_DECAY : ST_MUTED; + if(d_state == ST_MUTED) + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + + case ST_ATTACK: + // FIXME: precalculate window for speed + d_envelope = 0.5-std::cos(M_PI*(++d_ramped)/d_ramp)/2.0; + + // use >= in case d_ramp is set to lower value elsewhere + if(d_ramped >= d_ramp) { + d_state = ST_UNMUTED; + d_tag_next_unmuted = true; + d_envelope = 1.0; + } + break; + + case ST_DECAY: + // FIXME: precalculate window for speed + d_envelope = 0.5-std::cos(M_PI*(--d_ramped)/d_ramp)/2.0; + if(d_ramped == 0.0) { + d_state = ST_MUTED; + add_item_tag(0, nitems_written(0) + j, d_eob_key, pmt::PMT_NIL); + } + break; + }; + + // If unmuted, copy input times envelope to output + // Otherwise, if not gating, copy zero to output + if(d_state != ST_MUTED) + out[j++] = in[i]*d_envelope; + else + if(!d_gate) + out[j++] = 0.0; } consume_each(noutput_items); // Use all the inputs diff --git a/gr-analog/lib/squelch_base_ff_impl.h b/gr-analog/lib/squelch_base_ff_impl.h index 343dc5f610..b6a7efe609 100644 --- a/gr-analog/lib/squelch_base_ff_impl.h +++ b/gr-analog/lib/squelch_base_ff_impl.h @@ -24,6 +24,7 @@ #define INCLUDED_GR_SQUELCH_BASE_FF_IMPL_H #include <gnuradio/analog/squelch_base_ff.h> +#include <pmt/pmt.h> namespace gr { namespace analog { @@ -36,6 +37,8 @@ namespace gr { bool d_gate; double d_envelope; enum { ST_MUTED, ST_ATTACK, ST_UNMUTED, ST_DECAY } d_state; + const pmt::pmt_t d_sob_key, d_eob_key; + bool d_tag_next_unmuted; protected: virtual void update_state(const float &sample) {}; diff --git a/gr-blocks/examples/CMakeLists.txt b/gr-blocks/examples/CMakeLists.txt index bb07cdc2b5..0ecf9d7a91 100644 --- a/gr-blocks/examples/CMakeLists.txt +++ b/gr-blocks/examples/CMakeLists.txt @@ -20,6 +20,7 @@ install( FILES matrix_multiplexer.grc + peak_detector2.grc vector_source_with_tags.grc DESTINATION ${GR_PKG_DATA_DIR}/examples/blocks COMPONENT "runtime_python" diff --git a/gr-blocks/examples/peak_detector2.grc b/gr-blocks/examples/peak_detector2.grc new file mode 100644 index 0000000000..c49febdce4 --- /dev/null +++ b/gr-blocks/examples/peak_detector2.grc @@ -0,0 +1,1045 @@ +<?xml version='1.0' encoding='ASCII'?> +<?grc format='1' created='3.7.8'?> +<flow_graph> + <timestamp>Wed Apr 8 18:17:58 2015</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>test_peak2</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>run</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>thread_safe_setters</key> + <value></value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>factor</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0.3</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(264, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>alpha</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>0.001</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(440, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>lookahead</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>int(samp_rate/1e3/1.1)</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(352, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>100e3</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(176, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value>""</value> + </param> + <param> + <key>ylabel</key> + <value>Amplitude</value> + </param> + <param> + <key>yunit</key> + <value>""</value> + </param> + <param> + <key>size</key> + <value>512</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>grid</key> + <value>True</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>ymin</key> + <value>-1.5</value> + </param> + <param> + <key>ymax</key> + <value>1.5</value> + </param> + <param> + <key>nconnections</key> + <value>3</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>entags</key> + <value>True</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_AUTO</value> + </param> + <param> + <key>tr_slope</key> + <value>qtgui.TRIG_SLOPE_POS</value> + </param> + <param> + <key>tr_level</key> + <value>0.0</value> + </param> + <param> + <key>tr_delay</key> + <value>0</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>""</value> + </param> + <param> + <key>ctrlpanel</key> + <value>True</value> + </param> + <param> + <key>legend</key> + <value>True</value> + </param> + <param> + <key>label1</key> + <value>Input</value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>style1</key> + <value>1</value> + </param> + <param> + <key>marker1</key> + <value>-1</value> + </param> + <param> + <key>alpha1</key> + <value>1.0</value> + </param> + <param> + <key>label2</key> + <value>Peaks</value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>style2</key> + <value>1</value> + </param> + <param> + <key>marker2</key> + <value>-1</value> + </param> + <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> + <key>label3</key> + <value>Average</value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"green"</value> + </param> + <param> + <key>style3</key> + <value>1</value> + </param> + <param> + <key>marker3</key> + <value>-1</value> + </param> + <param> + <key>alpha3</key> + <value>1.0</value> + </param> + <param> + <key>label4</key> + <value></value> + </param> + <param> + <key>width4</key> + <value>1</value> + </param> + <param> + <key>color4</key> + <value>"black"</value> + </param> + <param> + <key>style4</key> + <value>1</value> + </param> + <param> + <key>marker4</key> + <value>-1</value> + </param> + <param> + <key>alpha4</key> + <value>1.0</value> + </param> + <param> + <key>label5</key> + <value></value> + </param> + <param> + <key>width5</key> + <value>1</value> + </param> + <param> + <key>color5</key> + <value>"cyan"</value> + </param> + <param> + <key>style5</key> + <value>1</value> + </param> + <param> + <key>marker5</key> + <value>-1</value> + </param> + <param> + <key>alpha5</key> + <value>1.0</value> + </param> + <param> + <key>label6</key> + <value></value> + </param> + <param> + <key>width6</key> + <value>1</value> + </param> + <param> + <key>color6</key> + <value>"magenta"</value> + </param> + <param> + <key>style6</key> + <value>1</value> + </param> + <param> + <key>marker6</key> + <value>-1</value> + </param> + <param> + <key>alpha6</key> + <value>1.0</value> + </param> + <param> + <key>label7</key> + <value></value> + </param> + <param> + <key>width7</key> + <value>1</value> + </param> + <param> + <key>color7</key> + <value>"yellow"</value> + </param> + <param> + <key>style7</key> + <value>1</value> + </param> + <param> + <key>marker7</key> + <value>-1</value> + </param> + <param> + <key>alpha7</key> + <value>1.0</value> + </param> + <param> + <key>label8</key> + <value></value> + </param> + <param> + <key>width8</key> + <value>1</value> + </param> + <param> + <key>color8</key> + <value>"dark red"</value> + </param> + <param> + <key>style8</key> + <value>1</value> + </param> + <param> + <key>marker8</key> + <value>-1</value> + </param> + <param> + <key>alpha8</key> + <value>1.0</value> + </param> + <param> + <key>label9</key> + <value></value> + </param> + <param> + <key>width9</key> + <value>1</value> + </param> + <param> + <key>color9</key> + <value>"dark green"</value> + </param> + <param> + <key>style9</key> + <value>1</value> + </param> + <param> + <key>marker9</key> + <value>-1</value> + </param> + <param> + <key>alpha9</key> + <value>1.0</value> + </param> + <param> + <key>label10</key> + <value></value> + </param> + <param> + <key>width10</key> + <value>1</value> + </param> + <param> + <key>color10</key> + <value>"blue"</value> + </param> + <param> + <key>style10</key> + <value>1</value> + </param> + <param> + <key>marker10</key> + <value>-1</value> + </param> + <param> + <key>alpha10</key> + <value>1.0</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(808, 152)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_char_to_float</key> + <param> + <key>id</key> + <value>blocks_char_to_float_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>1</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(624, 83)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_peak_detector2_fb</key> + <param> + <key>id</key> + <value>blocks_peak_detector2_fb_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>threshold_factor_rise</key> + <value>factor</value> + </param> + <param> + <key>look_ahead</key> + <value>lookahead</value> + </param> + <param> + <key>alpha</key> + <value>alpha</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 83)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_file_sink</key> + <param> + <key>id</key> + <value>blocks_file_sink_0_1</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>file</key> + <value>flag.data</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>unbuffered</key> + <value>False</value> + </param> + <param> + <key>append</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(808, 51)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_file_sink</key> + <param> + <key>id</key> + <value>blocks_file_sink_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>file</key> + <value>avg.data</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>unbuffered</key> + <value>False</value> + </param> + <param> + <key>append</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(784, 259)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_file_sink</key> + <param> + <key>id</key> + <value>blocks_file_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>file</key> + <value>in.data</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>unbuffered</key> + <value>False</value> + </param> + <param> + <key>append</key> + <value>False</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 187)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0.1</value> + </param> + <param> + <key>start</key> + <value>-1.5</value> + </param> + <param> + <key>stop</key> + <value>1.5</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 235)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_sig_source_x</key> + <param> + <key>id</key> + <value>analog_sig_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_SAW_WAVE</value> + </param> + <param> + <key>freq</key> + <value>1000</value> + </param> + <param> + <key>amp</key> + <value>1</value> + </param> + <param> + <key>offset</key> + <value>offset</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 115)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>ignoretag</key> + <value>True</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>comment</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(224, 147)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>blocks_char_to_float_0</source_block_id> + <sink_block_id>blocks_file_sink_0_1</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_peak_detector2_fb_0</source_block_id> + <sink_block_id>blocks_file_sink_0_0</sink_block_id> + <source_key>1</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_char_to_float_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_peak_detector2_fb_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0</sink_block_id> + <source_key>1</source_key> + <sink_key>2</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>blocks_peak_detector2_fb_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_peak_detector2_fb_0</source_block_id> + <sink_block_id>blocks_char_to_float_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>blocks_file_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-blocks/grc/blocks_peak_detector2_fb.xml b/gr-blocks/grc/blocks_peak_detector2_fb.xml index 584e7a1fb3..0b25e01680 100644 --- a/gr-blocks/grc/blocks_peak_detector2_fb.xml +++ b/gr-blocks/grc/blocks_peak_detector2_fb.xml @@ -38,4 +38,9 @@ <name>out</name> <type>byte</type> </source> + <source> + <name>debug</name> + <type>float</type> + <optional>1</optional> + </source> </block> diff --git a/gr-blocks/lib/ctrlport_probe2_b_impl.cc b/gr-blocks/lib/ctrlport_probe2_b_impl.cc index 3cf2ae0167..996e997d96 100644 --- a/gr-blocks/lib/ctrlport_probe2_b_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_b_impl.cc @@ -64,21 +64,26 @@ namespace gr { } std::vector<signed char> - ctrlport_probe2_b_impl::get() { + ctrlport_probe2_b_impl::get() + { return buffered_get.get(); } void ctrlport_probe2_b_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_b: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_b: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -94,20 +99,21 @@ namespace gr { { const char *in = (const char*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(char)); + d_index += num_copy; } // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - buffered_get.offer_data(d_buffer); + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } return noutput_items; diff --git a/gr-blocks/lib/ctrlport_probe2_b_impl.h b/gr-blocks/lib/ctrlport_probe2_b_impl.h index 490af9b040..165f0d33db 100644 --- a/gr-blocks/lib/ctrlport_probe2_b_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_b_impl.h @@ -38,6 +38,7 @@ namespace gr { size_t d_len; unsigned int d_disp_mask; + size_t d_index; std::vector<signed char> d_buffer; rpcbufferedget< std::vector<signed char> > buffered_get; @@ -64,4 +65,3 @@ namespace gr { } /* namespace gr */ #endif /* INCLUDED_CTRLPORT_PROBE2_C_IMPL_H */ - diff --git a/gr-blocks/lib/ctrlport_probe2_c_impl.cc b/gr-blocks/lib/ctrlport_probe2_c_impl.cc index bd43130be5..b8ed0af444 100644 --- a/gr-blocks/lib/ctrlport_probe2_c_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_c_impl.cc @@ -65,21 +65,26 @@ namespace gr { } std::vector<gr_complex> - ctrlport_probe2_c_impl::get() { + ctrlport_probe2_c_impl::get() + { return buffered_get.get(); } void ctrlport_probe2_c_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_c: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_c: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -95,20 +100,21 @@ namespace gr { { const gr_complex *in = (const gr_complex*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(gr_complex)); + d_index += num_copy; } // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - buffered_get.offer_data(d_buffer); + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } return noutput_items; diff --git a/gr-blocks/lib/ctrlport_probe2_c_impl.h b/gr-blocks/lib/ctrlport_probe2_c_impl.h index fa74216202..119738a481 100644 --- a/gr-blocks/lib/ctrlport_probe2_c_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_c_impl.h @@ -38,6 +38,7 @@ namespace gr { size_t d_len; unsigned int d_disp_mask; + size_t d_index; std::vector<gr_complex> d_buffer; rpcbufferedget< std::vector<gr_complex> > buffered_get; diff --git a/gr-blocks/lib/ctrlport_probe2_f_impl.cc b/gr-blocks/lib/ctrlport_probe2_f_impl.cc index 05d67da9dd..ff37401e88 100644 --- a/gr-blocks/lib/ctrlport_probe2_f_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_f_impl.cc @@ -63,21 +63,26 @@ namespace gr { } std::vector<float> - ctrlport_probe2_f_impl::get() { + ctrlport_probe2_f_impl::get() + { return buffered_get.get(); } void ctrlport_probe2_f_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_f: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_f: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -93,21 +98,21 @@ namespace gr { { const float *in = (const float*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(float)); + d_index += num_copy; } - // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - buffered_get.offer_data(d_buffer); + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } return noutput_items; diff --git a/gr-blocks/lib/ctrlport_probe2_f_impl.h b/gr-blocks/lib/ctrlport_probe2_f_impl.h index 8d406db927..6aec0789f4 100644 --- a/gr-blocks/lib/ctrlport_probe2_f_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_f_impl.h @@ -38,6 +38,7 @@ namespace gr { size_t d_len; unsigned int d_disp_mask; + size_t d_index; std::vector<float> d_buffer; rpcbufferedget< std::vector<float> > buffered_get; diff --git a/gr-blocks/lib/ctrlport_probe2_i_impl.cc b/gr-blocks/lib/ctrlport_probe2_i_impl.cc index 086ebe7cf0..7e17d8e8fe 100644 --- a/gr-blocks/lib/ctrlport_probe2_i_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_i_impl.cc @@ -65,21 +65,26 @@ namespace gr { } std::vector<int> - ctrlport_probe2_i_impl::get() { + ctrlport_probe2_i_impl::get() + { return buffered_get.get(); } void ctrlport_probe2_i_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_i: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_i: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -94,20 +99,22 @@ namespace gr { gr_vector_void_star &output_items) { const int *in = (const int*)input_items[0]; + + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(int)); + d_index += num_copy; } // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { - buffered_get.offer_data(d_buffer); + if(d_index == d_len) { + buffered_get.offer_data(d_buffer); + d_index = 0; } return noutput_items; diff --git a/gr-blocks/lib/ctrlport_probe2_i_impl.h b/gr-blocks/lib/ctrlport_probe2_i_impl.h index 3a976550eb..2832af07ec 100644 --- a/gr-blocks/lib/ctrlport_probe2_i_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_i_impl.h @@ -38,6 +38,7 @@ namespace gr { size_t d_len; unsigned int d_disp_mask; + size_t d_index; std::vector<int> d_buffer; rpcbufferedget< std::vector<int> > buffered_get; diff --git a/gr-blocks/lib/ctrlport_probe2_s_impl.cc b/gr-blocks/lib/ctrlport_probe2_s_impl.cc index d6a15faaef..9924243db0 100644 --- a/gr-blocks/lib/ctrlport_probe2_s_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_s_impl.cc @@ -65,21 +65,26 @@ namespace gr { } std::vector<short> - ctrlport_probe2_s_impl::get() { + ctrlport_probe2_s_impl::get() + { return buffered_get.get(); } void ctrlport_probe2_s_impl::set_length(int len) { + gr::thread::scoped_lock guard(d_setlock); + if(len > 8191) { - std::cerr << "probe2_s: length " << len - << " exceeds maximum buffer size of 8191" << std::endl; + GR_LOG_WARN(d_logger, + boost::format("probe2_s: length %1% exceeds maximum" + " buffer size of 8191") % len); len = 8191; } d_len = len; - d_buffer.reserve(d_len); + d_buffer.resize(d_len); + d_index = 0; } int @@ -95,19 +100,19 @@ namespace gr { { const short *in = (const short*)input_items[0]; + gr::thread::scoped_lock guard(d_setlock); + // copy samples to get buffer if we need samples - if(d_buffer.size() < d_len) { + if(d_index < d_len) { // copy smaller of remaining buffer space and num inputs to work() - int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + int num_copy = std::min( (int)(d_len - d_index), noutput_items ); - // TODO: convert this to a copy operator for speed... - for(int i = 0; i < num_copy; i++) { - d_buffer.push_back(in[i]); - } + memcpy(&d_buffer[d_index], in, num_copy*sizeof(short)); + d_index += num_copy; } // notify the waiting get() if we fill up the buffer - if(d_buffer.size() == d_len) { + if(d_index == d_len) { buffered_get.offer_data(d_buffer); } diff --git a/gr-blocks/lib/ctrlport_probe2_s_impl.h b/gr-blocks/lib/ctrlport_probe2_s_impl.h index 49533ced6f..a608a7898c 100644 --- a/gr-blocks/lib/ctrlport_probe2_s_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_s_impl.h @@ -38,6 +38,7 @@ namespace gr { size_t d_len; unsigned int d_disp_mask; + size_t d_index; std::vector<short> d_buffer; rpcbufferedget< std::vector<short> > buffered_get; diff --git a/gr-blocks/lib/peak_detector2_fb_impl.cc b/gr-blocks/lib/peak_detector2_fb_impl.cc index 7ff7f542ec..dc13e66dbe 100644 --- a/gr-blocks/lib/peak_detector2_fb_impl.cc +++ b/gr-blocks/lib/peak_detector2_fb_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2007,2010,2013 Free Software Foundation, Inc. + * Copyright 2007,2010,2013,2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -45,15 +45,46 @@ namespace gr { : sync_block("peak_detector2_fb", io_signature::make(1, 1, sizeof(float)), io_signature::make2(1, 2, sizeof(char), sizeof(float))), - d_threshold_factor_rise(threshold_factor_rise), - d_look_ahead(look_ahead), d_alpha(alpha), d_avg(0.0f), d_found(false) + d_avg(0.0f), d_found(false) { + set_threshold_factor_rise(threshold_factor_rise); + set_look_ahead(look_ahead); + set_alpha(alpha); } peak_detector2_fb_impl::~peak_detector2_fb_impl() { } + void + peak_detector2_fb_impl::set_threshold_factor_rise(float thr) + { + gr::thread::scoped_lock lock(d_setlock); + d_threshold_factor_rise = thr; + invalidate(); + } + + void + peak_detector2_fb_impl::set_look_ahead(int look) + { + gr::thread::scoped_lock lock(d_setlock); + d_look_ahead = look; + invalidate(); + } + + void + peak_detector2_fb_impl::set_alpha(float alpha) + { + d_alpha = alpha; + } + + void + peak_detector2_fb_impl::invalidate() + { + d_found = false; + set_output_multiple(1); + } + int peak_detector2_fb_impl::work(int noutput_items, gr_vector_const_void_star &input_items, @@ -61,53 +92,53 @@ namespace gr { { float *iptr = (float *)input_items[0]; char *optr = (char *)output_items[0]; + float *sigout; + + if(output_items.size() == 2) + sigout = (float *)output_items[1]; memset(optr, 0, noutput_items*sizeof(char)); - for(int i = 0; i < noutput_items; i++) { - if(!d_found) { - // Have not yet detected presence of peak + gr::thread::scoped_lock lock(d_setlock); + + // have not crossed threshold yet + if(d_found==false) { + for(int i = 0; i < noutput_items; i++) { + d_avg = d_alpha*iptr[i] + (1.0f - d_alpha)*d_avg; + if(output_items.size() == 2) + sigout[i]=d_avg; if(iptr[i] > d_avg * (1.0f + d_threshold_factor_rise)) { d_found = true; - d_look_ahead_remaining = d_look_ahead; d_peak_val = -(float)INFINITY; - } - else { - d_avg = d_alpha*iptr[i] + (1.0f - d_alpha)*d_avg; + set_output_multiple(d_look_ahead); + return i; } } - else { - // Detected presence of peak + return noutput_items; + } // end d_found==false + + // can complete in this call + else if(noutput_items >= d_look_ahead) { + for(int i = 0; i < d_look_ahead; i++) { + d_avg = d_alpha*iptr[i] + (1.0f - d_alpha)*d_avg; + if(output_items.size() == 2) + sigout[i]=d_avg; if(iptr[i] > d_peak_val) { d_peak_val = iptr[i]; - d_peak_ind = i; - } - else if(d_look_ahead_remaining <= 0) { - optr[d_peak_ind] = 1; - d_found = false; - d_avg = iptr[i]; + d_peak_ind =i; } - - // Have not yet located peak, loop and keep searching. - d_look_ahead_remaining--; - } - - // Every iteration of the loop, write debugging signal out if - // connected: - if(output_items.size() == 2) { - float *sigout = (float *)output_items[1]; - sigout[i] = d_avg; } - } // loop - - if(!d_found) - return noutput_items; + optr[d_peak_ind] = 1; - // else if detected presence, keep searching during the next call to work. - int tmp = d_peak_ind; - d_peak_ind = 1; + // restart the search + invalidate(); + return d_look_ahead; + } // end can complete in this call - return tmp - 1; + // cannot complete in this call + else { + return 0; // ask for more + } } } /* namespace blocks */ diff --git a/gr-blocks/lib/peak_detector2_fb_impl.h b/gr-blocks/lib/peak_detector2_fb_impl.h index f5a8ac1a6b..4e16c93dac 100644 --- a/gr-blocks/lib/peak_detector2_fb_impl.h +++ b/gr-blocks/lib/peak_detector2_fb_impl.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2007,2013 Free Software Foundation, Inc. + * Copyright 2007,2013,2015 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -40,14 +40,16 @@ namespace gr { float d_avg; bool d_found; + void invalidate(); + public: peak_detector2_fb_impl(float threshold_factor_rise, int look_ahead, float alpha); ~peak_detector2_fb_impl(); - void set_threshold_factor_rise(float thr) { d_threshold_factor_rise = thr; } - void set_look_ahead(int look) { d_look_ahead = look; } - void set_alpha(float alpha) { d_alpha = alpha; } + void set_threshold_factor_rise(float thr); + void set_look_ahead(int look); + void set_alpha(float alpha); float threshold_factor_rise() { return d_threshold_factor_rise; } int look_ahead() { return d_look_ahead; } diff --git a/gr-blocks/python/blocks/qa_peak_detector2.py b/gr-blocks/python/blocks/qa_peak_detector2.py index 475897eac2..d6fd4fe95f 100644 --- a/gr-blocks/python/blocks/qa_peak_detector2.py +++ b/gr-blocks/python/blocks/qa_peak_detector2.py @@ -30,18 +30,20 @@ class test_peak_detector2(gr_unittest.TestCase): def tearDown(self): self.tb = None - def test_regen1(self): + def test_peak1(self): + #print "\n\nTEST 1" tb = self.tb - data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + n=10 + data = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,)+n*(0,) expected_result = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)+n*(0,) src = blocks.vector_source_f(data, False) - regen = blocks.peak_detector2_fb() + regen = blocks.peak_detector2_fb(7.0, 25, 0.001) dst = blocks.vector_sink_b() tb.connect(src, regen) @@ -52,5 +54,80 @@ class test_peak_detector2(gr_unittest.TestCase): self.assertEqual(expected_result, dst_data) + def test_peak2(self): + #print "\n\nTEST 2" + tb = self.tb + + n=10 + data = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,)+n*(0,) + + expected_result = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)+n*(0,) + + + src = blocks.vector_source_f(data, False) + regen = blocks.peak_detector2_fb(7.0, 1000, 0.001) # called with a LONG window + dst = blocks.vector_sink_b() + + tb.connect(src, regen) + tb.connect(regen, dst) + tb.run() + + dst_data = dst.data() + + # here we know that the block will terminate prematurely, so we compare only part of the expected_result + self.assertEqual(expected_result[0:len(dst_data)], dst_data) + + + def test_peak3(self): + #print "\n\nTEST 3" + tb = self.tb + + l = 8100 + m = 100 + n = 10 + data = l*(0,)+ (10,)+ m*(0,)+(100,)+ n*(0,) + expected_result = l*(0,)+ (0,)+ m*(0,)+(1,)+ n*(0,) + + + src = blocks.vector_source_f(data, False) + regen = blocks.peak_detector2_fb(7.0, 105, 0.001) + dst = blocks.vector_sink_b() + + tb.connect(src, regen) + tb.connect(regen, dst) + tb.run() + + dst_data = dst.data() + + self.assertEqual(expected_result, dst_data) + + + def test_peak4(self): + #print "\n\nTEST 4" + tb = self.tb + + l = 8100 + m = 100 + n = 10 + data = l*(0,)+ (10,)+ m*(0,)+(100,)+ n*(0,) + expected_result = l*(0,)+ (0,)+ m*(0,)+(1,)+ n*(0,) + + + src = blocks.vector_source_f(data, False) + regen = blocks.peak_detector2_fb(7.0, 150, 0.001) + dst = blocks.vector_sink_b() + + tb.connect(src, regen) + tb.connect(regen, dst) + tb.run() + + dst_data = dst.data() + + # here we know that the block will terminate prematurely, so we compare only part of the expected_result + self.assertEqual(expected_result[0:len(dst_data)], dst_data) + + if __name__ == '__main__': gr_unittest.run(test_peak_detector2, "test_peak_detector2.xml") diff --git a/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t b/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t index 43d422ba80..fd7b69060e 100644 --- a/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t +++ b/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t @@ -49,7 +49,15 @@ namespace gr { * directly to the head and tail of each burst. * * Length tags will be updated to include the length of any added - * zero padding or phasing symbols. + * zero padding or phasing symbols and will be placed at the + * beginning of the modified tagged stream. Any other tags found at + * the same offset as a length tag will also be placed at the + * beginning of the modified tagged stream, since these tags are + * assumed to be associated with the burst rather than a specific + * sample. For example, if "tx_time" tags are used to control + * bursts, their offsets should be consistent with their associated + * burst's length tags. Tags at other offsets will be placed with + * the samples on which they were found. * * \li input: stream of @I_TYPE@ * \li output: stream of @O_TYPE@ diff --git a/gr-digital/lib/burst_shaper_XX_impl.cc.t b/gr-digital/lib/burst_shaper_XX_impl.cc.t index bd92665701..85add49115 100644 --- a/gr-digital/lib/burst_shaper_XX_impl.cc.t +++ b/gr-digital/lib/burst_shaper_XX_impl.cc.t @@ -68,7 +68,7 @@ namespace gr { d_ncopy(0), d_limit(0), d_index(0), - d_nprocessed(0), + d_length_tag_offset(0), d_finished(false), d_state(STATE_WAIT) { @@ -94,8 +94,8 @@ namespace gr { void @IMPL_NAME@::forecast(int noutput_items, - gr_vector_int &ninput_items_required) { - //if(d_state == STATE_COPY + gr_vector_int &ninput_items_required) + { ninput_items_required[0] = noutput_items; } @@ -112,13 +112,11 @@ namespace gr { int nread = 0; int nspace = 0; int nskip = 0; - uint64_t curr_tag_index = nitems_read(0); + int curr_tag_index = 0; - std::vector<tag_t> length_tags, tags; + std::vector<tag_t> length_tags; get_tags_in_window(length_tags, 0, 0, ninput_items[0], d_length_tag_key); - get_tags_in_window(tags, 0, 0, ninput_items[0]); std::sort(length_tags.rbegin(), length_tags.rend(), tag_t::offset_compare); - std::sort(tags.rbegin(), tags.rend(), tag_t::offset_compare); while((nwritten < noutput_items) && (nread < ninput_items[0])) { if(d_finished) { @@ -128,12 +126,14 @@ namespace gr { nspace = noutput_items - nwritten; switch(d_state) { case(STATE_WAIT): - if(!tags.empty()) { - curr_tag_index = tags.back().offset; - d_ncopy = pmt::to_long(tags.back().value); - tags.pop_back(); - nskip = (int)(curr_tag_index - d_nprocessed); + if(!length_tags.empty()) { + d_length_tag_offset = length_tags.back().offset; + curr_tag_index = (int)(d_length_tag_offset - nitems_read(0)); + d_ncopy = pmt::to_long(length_tags.back().value); + length_tags.pop_back(); + nskip = curr_tag_index - nread; add_length_tag(nwritten); + propagate_tags(curr_tag_index, nwritten, 1, false); enter_prepad(); } else { @@ -144,7 +144,7 @@ namespace gr { boost::format("Dropping %1% samples") % nskip); nread += nskip; - d_nprocessed += nskip; + in += nskip; } break; @@ -189,19 +189,22 @@ namespace gr { } int - @IMPL_NAME@::prefix_length() const { + @IMPL_NAME@::prefix_length() const + { return (d_insert_phasing) ? d_nprepad + d_up_ramp.size() : d_nprepad; } int - @IMPL_NAME@::suffix_length() const { + @IMPL_NAME@::suffix_length() const + { return (d_insert_phasing) ? d_npostpad + d_down_ramp.size() : d_npostpad; } void - @IMPL_NAME@::write_padding(@O_TYPE@ *&dst, int &nwritten, int nspace) { + @IMPL_NAME@::write_padding(@O_TYPE@ *&dst, int &nwritten, int nspace) + { int nprocess = std::min(d_limit - d_index, nspace); std::memset(dst, 0x00, nprocess * sizeof(@O_TYPE@)); dst += nprocess; @@ -211,8 +214,10 @@ namespace gr { void @IMPL_NAME@::copy_items(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, - int &nread, int nspace) { + int &nread, int nspace) + { int nprocess = std::min(d_limit - d_index, nspace); + propagate_tags(nread, nwritten, nprocess); std::memcpy(dst, src, nprocess * sizeof(@O_TYPE@)); dst += nprocess; nwritten += nprocess; @@ -223,7 +228,8 @@ namespace gr { void @IMPL_NAME@::apply_ramp(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, - int &nread, int nspace) { + int &nread, int nspace) + { int nprocess = std::min(d_limit - d_index, nspace); @O_TYPE@ *phasing; const @O_TYPE@ *ramp; @@ -240,6 +246,7 @@ namespace gr { if(d_insert_phasing) std::memcpy(dst, phasing, nprocess * sizeof(@O_TYPE@)); else { + propagate_tags(nread, nwritten, nprocess); VOLK_MULT_@O_TYPE@(dst, src, ramp, nprocess); src += nprocess; nread += nprocess; @@ -260,34 +267,48 @@ namespace gr { } void - @IMPL_NAME@::propagate_tags(std::vector<tag_t> &tags, int offset) + @IMPL_NAME@::propagate_tags(int in_offset, int out_offset, int count, bool skip) { - // FIXME: need to handle offsets correctly - std::vector<tag_t>::iterator tag; - for(tag = tags.begin(); tag != tags.end(); tag++) { - tag_t new_tag = *tag; - new_tag.offset = nitems_written(0) + offset; - add_item_tag(0, new_tag); + uint64_t abs_start = nitems_read(0) + in_offset; + uint64_t abs_end = abs_start + count; + uint64_t abs_offset = nitems_written(0) + out_offset; + tag_t temp_tag; + + std::vector<tag_t> tags; + std::vector<tag_t>::iterator it; + + get_tags_in_range(tags, 0, abs_start, abs_end); + + for(it = tags.begin(); it != tags.end(); it++) { + if(!pmt::equal(it->key, d_length_tag_key)) { + if(skip && (it->offset == d_length_tag_offset)) + continue; + temp_tag = *it; + temp_tag.offset = abs_offset + it->offset - abs_start; + add_item_tag(0, temp_tag); + } } } void - @IMPL_NAME@::enter_wait() { + @IMPL_NAME@::enter_wait() + { d_finished = true; - d_nprocessed += d_ncopy; d_index = 0; d_state = STATE_WAIT; } void - @IMPL_NAME@::enter_prepad() { + @IMPL_NAME@::enter_prepad() + { d_limit = d_nprepad; d_index = 0; d_state = STATE_PREPAD; } void - @IMPL_NAME@::enter_rampup() { + @IMPL_NAME@::enter_rampup() + { if(d_insert_phasing) d_limit = d_up_ramp.size(); else @@ -297,7 +318,8 @@ namespace gr { } void - @IMPL_NAME@::enter_copy() { + @IMPL_NAME@::enter_copy() + { if(d_insert_phasing) d_limit = d_ncopy; else @@ -309,7 +331,8 @@ namespace gr { } void - @IMPL_NAME@::enter_rampdown() { + @IMPL_NAME@::enter_rampdown() + { if(d_insert_phasing) d_limit = d_down_ramp.size(); else @@ -319,7 +342,8 @@ namespace gr { } void - @IMPL_NAME@::enter_postpad() { + @IMPL_NAME@::enter_postpad() + { d_limit = d_npostpad; d_index = 0; d_state = STATE_POSTPAD; diff --git a/gr-digital/lib/burst_shaper_XX_impl.h.t b/gr-digital/lib/burst_shaper_XX_impl.h.t index 90c7df8e88..99ad7fb08a 100644 --- a/gr-digital/lib/burst_shaper_XX_impl.h.t +++ b/gr-digital/lib/burst_shaper_XX_impl.h.t @@ -48,7 +48,7 @@ namespace gr { int d_ncopy; int d_limit; int d_index; - uint64_t d_nprocessed; + uint64_t d_length_tag_offset; bool d_finished; state_t d_state; @@ -58,7 +58,7 @@ namespace gr { void apply_ramp(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, int &nread, int nspace); void add_length_tag(int offset); - void propagate_tags(std::vector<tag_t> &tags, int offset); + void propagate_tags(int in_offset, int out_offset, int count, bool skip=true); void enter_wait(); void enter_prepad(); void enter_rampup(); diff --git a/gr-digital/python/digital/qa_burst_shaper.py b/gr-digital/python/digital/qa_burst_shaper.py index d00c23007b..f85b79ceec 100755 --- a/gr-digital/python/digital/qa_burst_shaper.py +++ b/gr-digital/python/digital/qa_burst_shaper.py @@ -25,6 +25,7 @@ from gnuradio import gr, gr_unittest from gnuradio import blocks, digital import pmt import numpy as np +import sys def make_length_tag(offset, length): return gr.python_to_tag({'offset' : offset, @@ -32,13 +33,15 @@ def make_length_tag(offset, length): 'value' : pmt.from_long(length), 'srcid' : pmt.intern('qa_burst_shaper')}) +def make_tag(offset, key, value): + return gr.python_to_tag({'offset' : offset, + 'key' : pmt.intern(key), + 'value' : value, + 'srcid' : pmt.intern('qa_burst_shaper')}) + def compare_tags(a, b): - a = gr.tag_to_python(a) - b = gr.tag_to_python(b) - return a.key == b.key and a.offset == b.offset and \ - a.value == b.value - #return a.key == b.key and a.offset == b.offset and \ - # a.srcid == b.srcid and a.value == b.value + return a.offset == b.offset and pmt.equal(a.key, b.key) and \ + pmt.equal(a.value, b.value) class qa_burst_shaper (gr_unittest.TestCase): @@ -244,31 +247,94 @@ class qa_burst_shaper (gr_unittest.TestCase): prepad = 10 postpad = 10 length = 20 - data = np.ones(2*length + 10) # need 10 more to push things through + gap_len = 5 + data = np.arange(2*length + 10, + dtype=float) # need 10 more to push things through window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) - tags = (make_length_tag(0, length), make_length_tag(length + 5, length)) - expected = np.concatenate((np.zeros(prepad), window[0:5], - np.ones(length - len(window)), window[5:10], - np.zeros(postpad))) - etags = (make_length_tag(0, length + prepad + postpad), - make_length_tag(length + prepad + postpad, - length + prepad + postpad)) + ewindow = window * np.array([1,-1,1,-1,1,1,-1,1,-1,1],dtype=float) + tags = (make_length_tag(0, length), + make_length_tag(length + gap_len, length)) + expected = np.concatenate((np.zeros(prepad), ewindow[0:5], + np.arange(0, length, dtype=float), + ewindow[5:10], np.zeros(postpad), + np.zeros(prepad), ewindow[0:5], + np.arange(length + gap_len, + 2*length + gap_len, dtype=float), + ewindow[5:10], np.zeros(postpad))) + burst_len = length + len(window) + prepad + postpad + etags = (make_length_tag(0, burst_len), + make_length_tag(burst_len, burst_len)) # flowgraph source = blocks.vector_source_f(data, tags=tags) shaper = digital.burst_shaper_ff(window, pre_padding=prepad, - post_padding=postpad) + post_padding=postpad, + insert_phasing=True) sink = blocks.vector_sink_f() self.tb.connect(source, shaper, sink) self.tb.run () # checks - self.assertFloatTuplesAlmostEqual(sink.data(), - np.concatenate((expected, expected), - 6)) + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) for i in xrange(len(etags)): self.assertTrue(compare_tags(sink.tags()[i], etags[i])) + def test_tag_propagation (self): + prepad = 10 + postpad = 10 + length1 = 15 + length2 = 25 + gap_len = 5 + lentag1_offset = 0 + lentag2_offset = length1 + gap_len + tag1_offset = 0 # accompanies first length tag + tag2_offset = length1 + gap_len # accompanies second length tag + tag3_offset = 2 # in ramp-up state + tag4_offset = length1 + 2 # in gap; tag will be dropped + tag5_offset = length1 + gap_len + 7 # in copy state + + data = np.concatenate((np.ones(length1), np.zeros(gap_len), + -1.0*np.ones(length2), np.zeros(10))) + window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) + tags = (make_length_tag(lentag1_offset, length1), + make_length_tag(lentag2_offset, length2), + make_tag(tag1_offset, 'head', pmt.intern('tag1')), + make_tag(tag2_offset, 'head', pmt.intern('tag2')), + make_tag(tag3_offset, 'body', pmt.intern('tag3')), + make_tag(tag4_offset, 'body', pmt.intern('tag4')), + make_tag(tag5_offset, 'body', pmt.intern('tag5'))) + expected = np.concatenate((np.zeros(prepad), window[0:5], + np.ones(length1 - len(window)), window[5:10], + np.zeros(postpad + prepad), -1.0*window[0:5], + -1.0*np.ones(length2 - len(window)), + -1.0*window[5:10], np.zeros(postpad))) + elentag1_offset = 0 + elentag2_offset = length1 + prepad + postpad + etag1_offset = 0 + etag2_offset = elentag2_offset + etag3_offset = prepad + tag3_offset + etag5_offset = 2*prepad + postpad + tag5_offset - gap_len + etags = (make_length_tag(elentag1_offset, length1 + prepad + postpad), + make_length_tag(elentag2_offset, length2 + prepad + postpad), + make_tag(etag1_offset, 'head', pmt.intern('tag1')), + make_tag(etag2_offset, 'head', pmt.intern('tag2')), + make_tag(etag3_offset, 'body', pmt.intern('tag3')), + make_tag(etag5_offset, 'body', pmt.intern('tag5'))) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + for x, y in zip(sorted(sink.tags(), key=gr.tag_t_offset_compare_key()), + sorted(etags, key=gr.tag_t_offset_compare_key())): + self.assertTrue(compare_tags(x, y)) + if __name__ == '__main__': gr_unittest.run(qa_burst_shaper, "qa_burst_shaper.xml") diff --git a/gr-qtgui/grc/qtgui_range.xml b/gr-qtgui/grc/qtgui_range.xml index 05f3ffce9f..71b614cc5e 100644 --- a/gr-qtgui/grc/qtgui_range.xml +++ b/gr-qtgui/grc/qtgui_range.xml @@ -6,105 +6,126 @@ ################################################### --> <block> - <name>QT GUI Range</name> - <key>variable_qtgui_range</key> - <import>from gnuradio.qtgui import Range, RangeWidget</import> - <var_make>self.$(id) = $(id) = $value</var_make> - <make>#set $win = 'self._%s_win'%$id + <name>QT GUI Range</name> + <key>variable_qtgui_range</key> + <import>from gnuradio.qtgui import Range, RangeWidget</import> + <var_make>self.$(id) = $(id) = $value</var_make> + <make>#set $win = 'self._%s_win'%$id #set $range = 'self._%s_range'%$id #if not $label() #set $label = '"%s"'%$id #end if $(range) = Range($start, $stop, $step, $value, $min_len) -$(win) = RangeWidget($range, self.set_$(id), $label, "$widget") +$(win) = RangeWidget($range, self.set_$(id), $label, "$widget", $rangeType) $(gui_hint()($win))</make> - <callback>self.set_$(id)($value)</callback> - <param> - <name>Label</name> - <key>label</key> - <value></value> - <type>string</type> - <hide>#if $label() then 'none' else 'part'#</hide> - </param> - <param> - <name>Default Value</name> - <key>value</key> - <value>50</value> - <type>real</type> - </param> - <param> - <name>Start</name> - <key>start</key> - <value>0</value> - <type>real</type> - </param> - <param> - <name>Stop</name> - <key>stop</key> - <value>100</value> - <type>real</type> - </param> - <param> - <name>Step</name> - <key>step</key> - <value>1</value> - <type>real</type> - </param> - <param> - <name>Widget</name> - <key>widget</key> - <value>counter_slider</value> - <type>enum</type> - <hide>part</hide> - <option><name>Counter + Slider</name><key>counter_slider</key></option> - <option><name>Counter</name><key>counter</key></option> - <option><name>Slider</name><key>slider</key></option> - <option><name>Knob</name><key>dial</key></option> - </param> - <param> - <name>Orientation</name> - <key>orient</key> - <value>Qt.Horizontal</value> - <type>enum</type> - <hide>#if $widget() == "slider" then 'part' else 'all'#</hide> - <option> - <name>Horizontal</name> - <key>Qt.Horizontal</key> - <opt>scalepos:BottomScale</opt> - <opt>minfcn:setMinimumWidth</opt> - </option> - <option> - <name>Vertical</name> - <key>Qt.Vertical</key> - <opt>scalepos:LeftScale</opt> - <opt>minfcn:setMinimumHeight</opt> - </option> - </param> - <param> - <name>Minimum Length</name> - <key>min_len</key> - <value>200</value> - <type>int</type> - <hide>part</hide> - </param> -<!-- from min_len <hide>#if $widget().split('_')[0] in ("slider", "counter") then 'part' else 'all'#</hide>--> - <param> - <name>GUI Hint</name> - <key>gui_hint</key> - <value></value> - <type>gui_hint</type> - <hide>part</hide> - </param> - <check>$start <= $value <= $stop</check> - <check>$start < $stop</check> - <doc> -This block creates a variable with a slider. \ -Leave the label blank to use the variable id as the label. \ -The value must be a real number. \ -The value must be between the start and the stop. + <callback>self.set_$(id)($value)</callback> -The GUI hint can be used to position the widget within the application. \ -The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ -Both the tab specification and the grid position are optional. - </doc> + <param> + <name>Label</name> + <key>label</key> + <value></value> + <type>string</type> + <hide>#if $label() then 'none' else 'part'#</hide> + </param> + + <param> + <name>Type</name> + <key>rangeType</key> + <value>"float"</value> + <type>enum</type> + <hide>part</hide> + <option><name>Float</name><key>float</key><opt>type:float</opt></option> + <option><name>Int</name><key>int</key><opt>type:int</opt></option> + </param> + + <param> + <name>Default Value</name> + <key>value</key> + <value>50</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Start</name> + <key>start</key> + <value>0</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Stop</name> + <key>stop</key> + <value>100</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Step</name> + <key>step</key> + <value>1</value> + <type>$rangeType.type</type> + </param> + + <param> + <name>Widget</name> + <key>widget</key> + <value>counter_slider</value> + <type>enum</type> + <hide>part</hide> + <option><name>Counter + Slider</name><key>counter_slider</key></option> + <option><name>Counter</name><key>counter</key></option> + <option><name>Slider</name><key>slider</key></option> + <option><name>Knob</name><key>dial</key></option> + </param> + + <param> + <name>Orientation</name> + <key>orient</key> + <value>Qt.Horizontal</value> + <type>enum</type> + <hide>#if $widget() == "slider" then 'part' else 'all'#</hide> + <option> + <name>Horizontal</name> + <key>Qt.Horizontal</key> + <opt>scalepos:BottomScale</opt> + <opt>minfcn:setMinimumWidth</opt> + </option> + <option> + <name>Vertical</name> + <key>Qt.Vertical</key> + <opt>scalepos:LeftScale</opt> + <opt>minfcn:setMinimumHeight</opt> + </option> + </param> + + <param> + <name>Minimum Length</name> + <key>min_len</key> + <value>200</value> + <type>int</type> + <hide>part</hide> + </param> + <!-- from min_len <hide>#if $widget().split('_')[0] in ("slider", "counter") then 'part' else 'all'#</hide>--> + + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + + <check>$start <= $value <= $stop</check> + <check>$start < $stop</check> + + <doc> + This block creates a variable with a slider. \ + Leave the label blank to use the variable id as the label. \ + The value must be a real number. \ + The value must be between the start and the stop. + + The GUI hint can be used to position the widget within the application. \ + The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ + Both the tab specification and the grid position are optional. + </doc> </block> diff --git a/gr-qtgui/python/qtgui/range.py b/gr-qtgui/python/qtgui/range.py index 168e6662c3..3eafc9002e 100755 --- a/gr-qtgui/python/qtgui/range.py +++ b/gr-qtgui/python/qtgui/range.py @@ -64,13 +64,16 @@ class Range(object): class RangeWidget(QtGui.QWidget): - def __init__(self, ranges, slot, label, style): + def __init__(self, ranges, slot, label, style, rangeType=float): """ Creates the QT Range widget """ QtGui.QWidget.__init__(self) self.range = ranges self.style = style + # rangeType tells the block how to return the value as a standard + self.rangeType = rangeType + # Top-block function to call when any value changes # Some widgets call this directly when their value changes. # Others have intermediate functions to map the value into the right range. @@ -81,24 +84,26 @@ class RangeWidget(QtGui.QWidget): layout.addWidget(label) if style == "dial": - self.d_widget = self.Dial(self, self.range, self.notifyChanged) + self.d_widget = self.Dial(self, self.range, self.notifyChanged, rangeType) elif style == "slider": - self.d_widget = self.Slider(self, self.range, self.notifyChanged) + self.d_widget = self.Slider(self, self.range, self.notifyChanged, rangeType) elif style == "counter": # The counter widget can be directly wired to the notifyChanged slot - self.d_widget = self.Counter(self, self.range, self.notifyChanged) + self.d_widget = self.Counter(self, self.range, self.notifyChanged, rangeType) else: # The CounterSlider needs its own internal handlers before calling notifyChanged - self.d_widget = self.CounterSlider(self, self.range, self.notifyChanged) + self.d_widget = self.CounterSlider(self, self.range, self.notifyChanged, rangeType) layout.addWidget(self.d_widget) self.setLayout(layout) class Dial(QtGui.QDial): """ Creates the range using a dial """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QDial.__init__(self, parent) + self.rangeType = rangeType + # Setup the dial self.setRange(0, ranges.nsteps-1) self.setSingleStep(1) @@ -116,13 +121,15 @@ class RangeWidget(QtGui.QWidget): def changed(self, value): """ Handles maping the value to the right range before calling the slot. """ val = self.range.map_range(value) - self.notifyChanged(val) + self.notifyChanged(self.rangeType(val)) class Slider(QtGui.QSlider): """ Creates the range using a slider """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QSlider.__init__(self, QtCore.Qt.Horizontal, parent) + self.rangeType = rangeType + # Setup the slider #self.setFocusPolicy(QtCore.Qt.NoFocus) self.setRange(0, ranges.nsteps - 1) @@ -149,7 +156,7 @@ class RangeWidget(QtGui.QWidget): def changed(self, value): """ Handle the valueChanged signal and map the value into the correct range """ val = self.range.map_range(value) - self.notifyChanged(val) + self.notifyChanged(self.rangeType(val)) def mousePressEvent(self, event): if((event.button() == QtCore.Qt.LeftButton)): @@ -168,9 +175,11 @@ class RangeWidget(QtGui.QWidget): class Counter(QtGui.QDoubleSpinBox): """ Creates the range using a counter """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QDoubleSpinBox.__init__(self, parent) + self.rangeType = rangeType + # Setup the counter self.setRange(ranges.min, ranges.max) self.setValue(ranges.default) @@ -179,18 +188,25 @@ class RangeWidget(QtGui.QWidget): self.setDecimals(ranges.precision) # The counter already handles floats and can be connected directly. - self.valueChanged.connect(slot) + self.valueChanged.connect(self.changed) + self.notifyChanged = slot + + def changed(self, value): + """ Handle the valueChanged signal by converting to the right type """ + self.notifyChanged(self.rangeType(value)) class CounterSlider(QtGui.QWidget): """ Creates the range using a counter and slider """ - def __init__(self, parent, ranges, slot): + def __init__(self, parent, ranges, slot, rangeType=float): QtGui.QWidget.__init__(self, parent) + self.rangeType = rangeType + # Slot to call in the parent self.notifyChanged = slot - self.slider = RangeWidget.Slider(parent, ranges, self.sliderChanged) - self.counter = RangeWidget.Counter(parent, ranges, self.counterChanged) + self.slider = RangeWidget.Slider(parent, ranges, self.sliderChanged, rangeType) + self.counter = RangeWidget.Counter(parent, ranges, self.counterChanged, rangeType) # Need another horizontal layout to wrap the other widgets. layout = Qt.QHBoxLayout() @@ -207,8 +223,8 @@ class RangeWidget(QtGui.QWidget): # If the counter was changed, ignore any of these events if not self.ignoreSlider: # Value is already float. Just set the counter - self.counter.setValue(value) - self.notifyChanged(value) + self.counter.setValue(self.rangeType(value)) + self.notifyChanged(self.rangeType(value)) self.ignoreSlider = False def counterChanged(self, value): @@ -223,4 +239,21 @@ class RangeWidget(QtGui.QWidget): self.ignoreSlider = True self.slider.setValue(new) - self.notifyChanged(value) + self.notifyChanged(self.rangeType(value)) + + +if __name__ == "__main__": + from PyQt4 import Qt + import sys + + def valueChanged(frequency): + print("Value updated - " + str(frequency)) + + app = Qt.QApplication(sys.argv) + widget = RangeWidget(Range(0, 100, 10, 1, 100), valueChanged, "Test", "counter_slider", int) + + widget.show() + widget.setWindowTitle("Test Qt Range") + app.exec_() + + widget = None diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion index 7a407eaaf8..b8b960a91e 100755 --- a/grc/scripts/gnuradio-companion +++ b/grc/scripts/gnuradio-companion @@ -55,6 +55,14 @@ def show_gtk_error_dialog(title, message): d.run() +def check_gtk_init(): + try: + gtk.init_check() + except RuntimeError: + print 'GTK initialization failed - bailing' + exit(-1) + + def check_gnuradio_import(): try: from gnuradio import gr @@ -108,6 +116,7 @@ def main(): if __name__ == '__main__': + check_gtk_init() check_gnuradio_import() ensure_blocks_path() main() |