diff options
author | Tom Rondeau <tom@trondeau.com> | 2014-04-24 14:10:26 -0400 |
---|---|---|
committer | Tom Rondeau <tom@trondeau.com> | 2014-04-24 15:21:54 -0400 |
commit | d78d1ce2069ae20c86f9f301d89f303c2ae5942e (patch) | |
tree | 1bd2b6b5728b7bd1356b6ad260b7eb5bfbcd8038 | |
parent | e2afce984c606a1373150c5f6f51a4b00cd0b47a (diff) |
qtgui: adding a trigger for the constellation plots. Probably most useful with tags, but normal and auto triggering supported, too.
Updates test_corr_and_sync block to use the time_est tag to trigger constellation plot.
-rw-r--r-- | gr-digital/examples/demod/test_corr_and_sync.grc | 176 | ||||
-rw-r--r-- | gr-qtgui/grc/qtgui_const_sink_x.xml | 79 | ||||
-rw-r--r-- | gr-qtgui/include/gnuradio/qtgui/const_sink_c.h | 41 | ||||
-rw-r--r-- | gr-qtgui/include/gnuradio/qtgui/constellationdisplayform.h | 31 | ||||
-rw-r--r-- | gr-qtgui/include/gnuradio/qtgui/time_sink_c.h | 49 | ||||
-rw-r--r-- | gr-qtgui/include/gnuradio/qtgui/time_sink_f.h | 41 | ||||
-rw-r--r-- | gr-qtgui/lib/const_sink_c_impl.cc | 272 | ||||
-rw-r--r-- | gr-qtgui/lib/const_sink_c_impl.h | 27 | ||||
-rw-r--r-- | gr-qtgui/lib/constellationdisplayform.cc | 138 | ||||
-rw-r--r-- | gr-qtgui/lib/time_sink_c_impl.cc | 2 |
10 files changed, 692 insertions, 164 deletions
diff --git a/gr-digital/examples/demod/test_corr_and_sync.grc b/gr-digital/examples/demod/test_corr_and_sync.grc index 8701690b88..496a0be356 100644 --- a/gr-digital/examples/demod/test_corr_and_sync.grc +++ b/gr-digital/examples/demod/test_corr_and_sync.grc @@ -1,6 +1,6 @@ <?xml version='1.0' encoding='ASCII'?> <flow_graph> - <timestamp>Thu Apr 10 15:34:59 2014</timestamp> + <timestamp>Thu Apr 24 14:08:23 2014</timestamp> <block> <key>options</key> <param> @@ -64,30 +64,7 @@ <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>100000</value> - </param> - <param> - <key>_coordinate</key> - <value>(11, 72)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable</key> - <param> - <key>id</key> - <value>eb</value> + <value>rrc_taps</value> </param> <param> <key>_enabled</key> @@ -95,11 +72,11 @@ </param> <param> <key>value</key> - <value>0.35</value> + <value>firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), eb, 5*sps*nfilts)</value> </param> <param> <key>_coordinate</key> - <value>(346, 72)</value> + <value>(1075, 73)</value> </param> <param> <key>_rotation</key> @@ -110,7 +87,7 @@ <key>variable</key> <param> <key>id</key> - <value>matched_filter</value> + <value>nfilts</value> </param> <param> <key>_enabled</key> @@ -118,11 +95,11 @@ </param> <param> <key>value</key> - <value>firdes.root_raised_cosine(nfilts, nfilts, 1, eb, int(11*sps*nfilts))</value> + <value>32</value> </param> <param> <key>_coordinate</key> - <value>(429, 72)</value> + <value>(1074, 9)</value> </param> <param> <key>_rotation</key> @@ -133,7 +110,7 @@ <key>variable</key> <param> <key>id</key> - <value>preamble</value> + <value>payload_size</value> </param> <param> <key>_enabled</key> @@ -141,11 +118,11 @@ </param> <param> <key>value</key> - <value>[1,-1,1,-1,1,1,-1,-1,1,1,-1,1,1,1,-1,1,1,-1,1,-1,-1,1,-1,-1,1,1,1,-1,-1,-1,1,-1,1,1,1,1,-1,-1,1,-1,1,-1,-1,-1,1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1]</value> + <value>992</value> </param> <param> <key>_coordinate</key> - <value>(279, 8)</value> + <value>(101, 73)</value> </param> <param> <key>_rotation</key> @@ -156,7 +133,7 @@ <key>variable</key> <param> <key>id</key> - <value>sps</value> + <value>gap</value> </param> <param> <key>_enabled</key> @@ -164,11 +141,11 @@ </param> <param> <key>value</key> - <value>4</value> + <value>20000</value> </param> <param> <key>_coordinate</key> - <value>(278, 72)</value> + <value>(202, 72)</value> </param> <param> <key>_rotation</key> @@ -202,7 +179,7 @@ <key>variable</key> <param> <key>id</key> - <value>gap</value> + <value>sps</value> </param> <param> <key>_enabled</key> @@ -210,11 +187,11 @@ </param> <param> <key>value</key> - <value>20000</value> + <value>4</value> </param> <param> <key>_coordinate</key> - <value>(202, 72)</value> + <value>(278, 72)</value> </param> <param> <key>_rotation</key> @@ -225,7 +202,7 @@ <key>variable</key> <param> <key>id</key> - <value>payload_size</value> + <value>preamble</value> </param> <param> <key>_enabled</key> @@ -233,11 +210,11 @@ </param> <param> <key>value</key> - <value>992</value> + <value>[1,-1,1,-1,1,1,-1,-1,1,1,-1,1,1,1,-1,1,1,-1,1,-1,-1,1,-1,-1,1,1,1,-1,-1,-1,1,-1,1,1,1,1,-1,-1,1,-1,1,-1,-1,-1,1,1,-1,-1,-1,-1,1,-1,-1,-1,-1,-1,1,1,1,1,1,1,-1,-1]</value> </param> <param> <key>_coordinate</key> - <value>(101, 73)</value> + <value>(279, 8)</value> </param> <param> <key>_rotation</key> @@ -248,7 +225,7 @@ <key>variable</key> <param> <key>id</key> - <value>nfilts</value> + <value>matched_filter</value> </param> <param> <key>_enabled</key> @@ -256,11 +233,11 @@ </param> <param> <key>value</key> - <value>32</value> + <value>firdes.root_raised_cosine(nfilts, nfilts, 1, eb, int(11*sps*nfilts))</value> </param> <param> <key>_coordinate</key> - <value>(1074, 9)</value> + <value>(429, 72)</value> </param> <param> <key>_rotation</key> @@ -271,7 +248,7 @@ <key>variable</key> <param> <key>id</key> - <value>rrc_taps</value> + <value>eb</value> </param> <param> <key>_enabled</key> @@ -279,11 +256,11 @@ </param> <param> <key>value</key> - <value>firdes.root_raised_cosine(nfilts, nfilts, 1.0/float(sps), eb, 5*sps*nfilts)</value> + <value>0.35</value> </param> <param> <key>_coordinate</key> - <value>(1075, 73)</value> + <value>(346, 72)</value> </param> <param> <key>_rotation</key> @@ -291,38 +268,22 @@ </param> </block> <block> - <key>digital_costas_loop_cc</key> + <key>variable</key> <param> <key>id</key> - <value>digital_costas_loop_cc_0</value> + <value>samp_rate</value> </param> <param> <key>_enabled</key> <value>True</value> </param> <param> - <key>w</key> - <value>1*3.14/50.0</value> - </param> - <param> - <key>order</key> - <value>2</value> - </param> - <param> - <key>affinity</key> - <value></value> - </param> - <param> - <key>minoutbuf</key> - <value>0</value> - </param> - <param> - <key>maxoutbuf</key> - <value>0</value> + <key>value</key> + <value>100000</value> </param> <param> <key>_coordinate</key> - <value>(1091, 303)</value> + <value>(11, 72)</value> </param> <param> <key>_rotation</key> @@ -2611,10 +2572,49 @@ </param> </block> <block> + <key>digital_costas_loop_cc</key> + <param> + <key>id</key> + <value>digital_costas_loop_cc_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>w</key> + <value>1*3.14/50.0</value> + </param> + <param> + <key>order</key> + <value>2</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>_coordinate</key> + <value>(1091, 303)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> <key>qtgui_const_sink_x</key> <param> <key>id</key> - <value>qtgui_const_sink_x_0_0</value> + <value>qtgui_const_sink_x_0</value> </param> <param> <key>_enabled</key> @@ -2630,7 +2630,7 @@ </param> <param> <key>size</key> - <value>20000</value> + <value>1000</value> </param> <param> <key>autoscale</key> @@ -2665,6 +2665,26 @@ <value>0,1,1,1</value> </param> <param> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_TAG</value> + </param> + <param> + <key>tr_slope</key> + <value>qtgui.TRIG_SLOPE_POS</value> + </param> + <param> + <key>tr_level</key> + <value>0</value> + </param> + <param> + <key>tr_chan</key> + <value>0</value> + </param> + <param> + <key>tr_tag</key> + <value>time_est</value> + </param> + <param> <key>label1</key> <value></value> </param> @@ -2910,7 +2930,7 @@ </param> <param> <key>_coordinate</key> - <value>(1399, 291)</value> + <value>(1399, 283)</value> </param> <param> <key>_rotation</key> @@ -2919,12 +2939,6 @@ </block> <connection> <source_block_id>digital_costas_loop_cc_0</source_block_id> - <sink_block_id>qtgui_const_sink_x_0_0</sink_block_id> - <source_key>0</source_key> - <sink_key>0</sink_key> - </connection> - <connection> - <source_block_id>digital_costas_loop_cc_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> @@ -3097,4 +3111,10 @@ <source_key>0</source_key> <sink_key>0</sink_key> </connection> + <connection> + <source_block_id>digital_costas_loop_cc_0</source_block_id> + <sink_block_id>qtgui_const_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> </flow_graph> diff --git a/gr-qtgui/grc/qtgui_const_sink_x.xml b/gr-qtgui/grc/qtgui_const_sink_x.xml index 08f9064648..3b549e2d31 100644 --- a/gr-qtgui/grc/qtgui_const_sink_x.xml +++ b/gr-qtgui/grc/qtgui_const_sink_x.xml @@ -19,6 +19,7 @@ qtgui.$(type.fcn)( self.$(id).set_update_time($update_time) self.$(id).set_y_axis($ymin, $ymax) self.$(id).set_x_axis($xmin, $xmax) +self.$(id).set_trigger_mode($tr_mode, $tr_slope, $tr_level, $tr_chan, $tr_tag) self.$(id).enable_autoscale($autoscale) labels = [$label1, $label2, $label3, $label4, $label5, @@ -51,6 +52,12 @@ $(gui_hint()($win))</make> <callback>set_title($which, $title)</callback> <callback>set_color($which, $color)</callback> + <param_tab_order> + <tab>General</tab> + <tab>Trigger</tab> + <tab>Config</tab> + </param_tab_order> + <param> <name>Type</name> <key>type</key> @@ -137,6 +144,78 @@ $(gui_hint()($win))</make> <hide>part</hide> </param> + + <param> + <name>Trigger Mode</name> + <key>tr_mode</key> + <value>qtgui.TRIG_MODE_FREE</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Free</name> + <key>qtgui.TRIG_MODE_FREE</key> + </option> + <option> + <name>Auto</name> + <key>qtgui.TRIG_MODE_AUTO</key> + </option> + <option> + <name>Normal</name> + <key>qtgui.TRIG_MODE_NORM</key> + </option> + <option> + <name>Tag</name> + <key>qtgui.TRIG_MODE_TAG</key> + </option> + <tab>Trigger</tab> + </param> + + <param> + <name>Trigger Slope</name> + <key>tr_slope</key> + <value>qtgui.TRIG_MODE_POS</value> + <type>enum</type> + <hide>part</hide> + <option> + <name>Positive</name> + <key>qtgui.TRIG_SLOPE_POS</key> + </option> + <option> + <name>Negative</name> + <key>qtgui.TRIG_SLOPE_NEG</key> + </option> + <tab>Trigger</tab> + </param> + + <param> + <name>Trigger Level</name> + <key>tr_level</key> + <value>0.0</value> + <type>float</type> + <hide>part</hide> + <tab>Trigger</tab> + </param> + + <param> + <name>Trigger Channel</name> + <key>tr_chan</key> + <value>0</value> + <type>int</type> + <hide>part</hide> + <tab>Trigger</tab> + </param> + + <param> + <name>Trigger Tag Key</name> + <key>tr_tag</key> + <value>""</value> + <type>string</type> + <hide>part</hide> + <tab>Trigger</tab> + </param> + + + <param> <name>Line 1 Label</name> <key>label1</key> diff --git a/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h b/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h index f413ce9b54..84f9a87891 100644 --- a/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h +++ b/gr-qtgui/include/gnuradio/qtgui/const_sink_c.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -28,13 +28,14 @@ #endif #include <gnuradio/qtgui/api.h> +#include <gnuradio/qtgui/trigger_mode.h> #include <gnuradio/sync_block.h> #include <qapplication.h> #include <gnuradio/filter/firdes.h> namespace gr { namespace qtgui { - + /*! * \brief A graphical sink to display the IQ constellation of multiple signals. * \ingroup instrumentation_blk @@ -51,7 +52,7 @@ namespace gr { typedef boost::shared_ptr<const_sink_c> sptr; /*! - * \brief Build a complex PSD sink. + * \brief Build a constellation plot sink. * * \param size number of points to plot at once * \param name title for the plot @@ -85,6 +86,40 @@ namespace gr { virtual void set_nsamps(const int newsize) = 0; virtual void set_line_alpha(int which, double alpha) = 0; + /*! + * Set up a trigger for the sink to know when to start + * plotting. Useful to isolate events and avoid noise. + * + * The trigger modes are Free, Auto, Normal, and Tag (see + * gr::qtgui::trigger_mode). The first three are like a normal + * oscope trigger function. Free means free running with no + * trigger, auto will trigger if the trigger event is seen, but + * will still plot otherwise, and normal will hold until the + * trigger event is observed. The Tag trigger mode allows us to + * trigger off a specific stream tag. The tag trigger is based + * only on the name of the tag, so when a tag of the given name + * is seen, the trigger is activated. + * + * In auto and normal mode, we look for the slope of the + * magnitude of the signal. As a constellation sink, this only + * takes in complex numbers to plot. Given a + * gr::qtgui::trigger_slope as either Positive or Negative, if + * the magnitude between two samples moves in the given + * direction (x[1] > x[0] for Positive or x[1] < x[0] for + * Negative), then the trigger is activated. + * + * \param mode The trigger_mode: free, auto, normal, or tag. + * \param slope The trigger_slope: positive or negative. Only + * used for auto and normal modes. + * \param level The magnitude of the trigger even for auto or normal modes. + * \param channel Which input channel to use for the trigger events. + * \param tag_key The name (as a string) of the tag to trigger off + * of if using the tag mode. + */ + virtual void set_trigger_mode(trigger_mode mode, trigger_slope slope, + float level, int channel, + const std::string &tag_key="") = 0; + virtual std::string title() = 0; virtual std::string line_label(int which) = 0; virtual std::string line_color(int which) = 0; diff --git a/gr-qtgui/include/gnuradio/qtgui/constellationdisplayform.h b/gr-qtgui/include/gnuradio/qtgui/constellationdisplayform.h index fb14388fce..e75eac9fb1 100644 --- a/gr-qtgui/include/gnuradio/qtgui/constellationdisplayform.h +++ b/gr-qtgui/include/gnuradio/qtgui/constellationdisplayform.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -46,6 +46,11 @@ public: ConstellationDisplayPlot* getPlot(); int getNPoints() const; + gr::qtgui::trigger_mode getTriggerMode() const; + gr::qtgui::trigger_slope getTriggerSlope() const; + float getTriggerLevel() const; + int getTriggerChannel() const; + std::string getTriggerTagKey() const; public slots: void customEvent(QEvent * e); @@ -56,12 +61,36 @@ public slots: void setXaxis(double min, double max); void autoScale(bool en); + void updateTrigger(gr::qtgui::trigger_mode mode); + void setTriggerMode(gr::qtgui::trigger_mode mode); + void setTriggerSlope(gr::qtgui::trigger_slope slope); + void setTriggerLevel(QString s); + void setTriggerLevel(float level); + void setTriggerChannel(int chan); + void setTriggerTagKey(QString s); + void setTriggerTagKey(const std::string &s); + private slots: void newData(const QEvent*); private: QIntValidator* d_int_validator; int d_npoints; + + NPointsMenu *d_nptsmenu; + + QMenu *d_triggermenu; + TriggerModeMenu *d_tr_mode_menu; + TriggerSlopeMenu *d_tr_slope_menu; + PopupMenu *d_tr_level_act; + TriggerChannelMenu *d_tr_channel_menu; + PopupMenu *d_tr_tag_key_act; + + gr::qtgui::trigger_mode d_trig_mode; + gr::qtgui::trigger_slope d_trig_slope; + float d_trig_level; + int d_trig_channel; + std::string d_trig_tag_key; }; #endif /* CONSTELLATION_DISPLAY_FORM_H */ diff --git a/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h b/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h index 2d5248552b..8ce6e2e84a 100644 --- a/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h +++ b/gr-qtgui/include/gnuradio/qtgui/time_sink_c.h @@ -34,7 +34,7 @@ namespace gr { namespace qtgui { - + /*! * \brief A graphical sink to display multiple signals in time. * \ingroup instrumentation_blk @@ -88,6 +88,53 @@ namespace gr { virtual void set_nsamps(const int newsize) = 0; virtual void set_samp_rate(const double samp_rate) = 0; virtual void set_line_alpha(int which, double alpha) = 0; + + /*! + * Set up a trigger for the sink to know when to start + * plotting. Useful to isolate events and avoid noise. + * + * The trigger modes are Free, Auto, Normal, and Tag (see + * gr::qtgui::trigger_mode). The first three are like a normal + * oscope trigger function. Free means free running with no + * trigger, auto will trigger if the trigger event is seen, but + * will still plot otherwise, and normal will hold until the + * trigger event is observed. The Tag trigger mode allows us to + * trigger off a specific stream tag. The tag trigger is based + * only on the name of the tag, so when a tag of the given name + * is seen, the trigger is activated. + * + * In auto and normal mode, we look for the slope of the of the + * signal. Given a gr::qtgui::trigger_slope as either Positive + * or Negative, if the value between two samples moves in the + * given direction (x[1] > x[0] for Positive or x[1] < x[0] for + * Negative), then the trigger is activated. + * + * With the complex time sink, each input has two lines drawn + * for the real and imaginary parts of the signal. When + * selecting the \p channel value, channel 0 is the real signal + * and channel 1 is the imaginary signal. For more than 1 input + * stream, channel 2i is the real part of the ith input and + * channel (2i+1) is the imaginary part of the ith input + * channel. + * + * The \p delay value is specified in time based off the sample + * rate. If the sample rate of the block is set to 1, the delay + * is then also the sample number offset. This is the offset + * from the left-hand y-axis of the plot. It delays the signal + * to show the trigger event at the given delay along with some + * portion of the signal before the event. The delay must be + * within 0 - t_max where t_max is the maximum amount of time + * displayed on the time plot. + * + * \param mode The trigger_mode: free, auto, normal, or tag. + * \param slope The trigger_slope: positive or negative. Only + * used for auto and normal modes. + * \param level The magnitude of the trigger even for auto or normal modes. + * \param delay The delay (in units of time) for where the trigger happens. + * \param channel Which input channel to use for the trigger events. + * \param tag_key The name (as a string) of the tag to trigger off + * of if using the tag mode. + */ virtual void set_trigger_mode(trigger_mode mode, trigger_slope slope, float level, float delay, int channel, const std::string &tag_key="") = 0; diff --git a/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h b/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h index add53ef603..1a0949247f 100644 --- a/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h +++ b/gr-qtgui/include/gnuradio/qtgui/time_sink_f.h @@ -34,7 +34,7 @@ namespace gr { namespace qtgui { - + /*! * \brief A graphical sink to display multiple signals in time. * \ingroup instrumentation_blk @@ -86,6 +86,45 @@ namespace gr { virtual void set_nsamps(const int newsize) = 0; virtual void set_samp_rate(const double samp_rate) = 0; virtual void set_line_alpha(int which, double alpha) = 0; + + /*! + * Set up a trigger for the sink to know when to start + * plotting. Useful to isolate events and avoid noise. + * + * The trigger modes are Free, Auto, Normal, and Tag (see + * gr::qtgui::trigger_mode). The first three are like a normal + * oscope trigger function. Free means free running with no + * trigger, auto will trigger if the trigger event is seen, but + * will still plot otherwise, and normal will hold until the + * trigger event is observed. The Tag trigger mode allows us to + * trigger off a specific stream tag. The tag trigger is based + * only on the name of the tag, so when a tag of the given name + * is seen, the trigger is activated. + * + * In auto and normal mode, we look for the slope of the of the + * signal. Given a gr::qtgui::trigger_slope as either Positive + * or Negative, if the value between two samples moves in the + * given direction (x[1] > x[0] for Positive or x[1] < x[0] for + * Negative), then the trigger is activated. + * + * The \p delay value is specified in time based off the sample + * rate. If the sample rate of the block is set to 1, the delay + * is then also the sample number offset. This is the offset + * from the left-hand y-axis of the plot. It delays the signal + * to show the trigger event at the given delay along with some + * portion of the signal before the event. The delay must be + * within 0 - t_max where t_max is the maximum amount of time + * displayed on the time plot. + * + * \param mode The trigger_mode: free, auto, normal, or tag. + * \param slope The trigger_slope: positive or negative. Only + * used for auto and normal modes. + * \param level The magnitude of the trigger even for auto or normal modes. + * \param delay The delay (in units of time) for where the trigger happens. + * \param channel Which input channel to use for the trigger events. + * \param tag_key The name (as a string) of the tag to trigger off + * of if using the tag mode. + */ virtual void set_trigger_mode(trigger_mode mode, trigger_slope slope, float level, float delay, int channel, const std::string &tag_key="") = 0; diff --git a/gr-qtgui/lib/const_sink_c_impl.cc b/gr-qtgui/lib/const_sink_c_impl.cc index fb0cdade6a..a703336a61 100644 --- a/gr-qtgui/lib/const_sink_c_impl.cc +++ b/gr-qtgui/lib/const_sink_c_impl.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -49,9 +49,9 @@ namespace gr { int nconnections, QWidget *parent) : sync_block("const_sink_c", - io_signature::make(nconnections, nconnections, sizeof(gr_complex)), - io_signature::make(0, 0, 0)), - d_size(size), d_name(name), + io_signature::make(nconnections, nconnections, sizeof(gr_complex)), + io_signature::make(0, 0, 0)), + d_size(size), d_buffer_size(2*size), d_name(name), d_nconnections(nconnections), d_parent(parent) { // Required now for Qt; argc must be greater than 0 and argv @@ -67,12 +67,12 @@ namespace gr { d_index = 0; for(int i = 0; i < d_nconnections; i++) { - d_residbufs_real.push_back((double*)volk_malloc(d_size*sizeof(double), + d_residbufs_real.push_back((double*)volk_malloc(d_buffer_size*sizeof(double), volk_get_alignment())); - d_residbufs_imag.push_back((double*)volk_malloc(d_size*sizeof(double), + d_residbufs_imag.push_back((double*)volk_malloc(d_buffer_size*sizeof(double), volk_get_alignment())); - memset(d_residbufs_real[i], 0, d_size*sizeof(double)); - memset(d_residbufs_imag[i], 0, d_size*sizeof(double)); + memset(d_residbufs_real[i], 0, d_buffer_size*sizeof(double)); + memset(d_residbufs_imag[i], 0, d_buffer_size*sizeof(double)); } // Set alignment properties for VOLK @@ -81,7 +81,12 @@ namespace gr { set_alignment(std::max(1,alignment_multiple)); initialize(); - } + + set_trigger_mode(TRIG_MODE_FREE, TRIG_SLOPE_POS, 0, 0); + + set_history(2); // so we can look ahead for the trigger slope + declare_sample_delay(1); // delay the tags for a history of 2 + } const_sink_c_impl::~const_sink_c_impl() { @@ -221,6 +226,32 @@ namespace gr { } void + const_sink_c_impl::set_trigger_mode(trigger_mode mode, + trigger_slope slope, + float level, + int channel, + const std::string &tag_key) + { + gr::thread::scoped_lock lock(d_mutex); + + d_trigger_mode = mode; + d_trigger_slope = slope; + d_trigger_level = level; + d_trigger_channel = channel; + d_trigger_tag_key = pmt::intern(tag_key); + d_triggered = false; + d_trigger_count = 0; + + d_main_gui->setTriggerMode(d_trigger_mode); + d_main_gui->setTriggerSlope(d_trigger_slope); + d_main_gui->setTriggerLevel(d_trigger_level); + d_main_gui->setTriggerChannel(d_trigger_channel); + d_main_gui->setTriggerTagKey(tag_key); + + _reset(); + } + + void const_sink_c_impl::set_size(int width, int height) { d_main_gui->resize(QSize(width, height)); @@ -274,35 +305,29 @@ namespace gr { gr::thread::scoped_lock lock(d_mutex); if(newsize != d_size) { + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) + d_size = newsize; + d_buffer_size = 2*d_size; + d_index = 0; + // Resize residbuf and replace data for(int i = 0; i < d_nconnections; i++) { volk_free(d_residbufs_real[i]); volk_free(d_residbufs_imag[i]); - d_residbufs_real[i] = (double*)volk_malloc(newsize*sizeof(double), + d_residbufs_real[i] = (double*)volk_malloc(d_buffer_size*sizeof(double), volk_get_alignment()); - d_residbufs_imag[i] = (double*)volk_malloc(newsize*sizeof(double), + d_residbufs_imag[i] = (double*)volk_malloc(d_buffer_size*sizeof(double), volk_get_alignment()); - memset(d_residbufs_real[i], 0, newsize*sizeof(double)); - memset(d_residbufs_imag[i], 0, newsize*sizeof(double)); + memset(d_residbufs_real[i], 0, d_buffer_size*sizeof(double)); + memset(d_residbufs_imag[i], 0, d_buffer_size*sizeof(double)); } - // Set new size and reset buffer index - // (throws away any currently held data, but who cares?) - d_size = newsize; - d_index = 0; - d_main_gui->setNPoints(d_size); } } - void - const_sink_c_impl::npoints_resize() - { - int newsize = d_main_gui->getNPoints(); - set_nsamps(newsize); - } - int const_sink_c_impl::nsamps() const { @@ -324,7 +349,104 @@ namespace gr { void const_sink_c_impl::reset() { + gr::thread::scoped_lock lock(d_mutex); + _reset(); + } + + void + const_sink_c_impl::_reset() + { + // Reset the start and end indices. + d_start = 0; + d_end = d_size; d_index = 0; + + // Reset the trigger. + if(d_trigger_mode == TRIG_MODE_FREE) { + d_triggered = true; + } + else { + d_triggered = false; + } + } + + void + const_sink_c_impl::_npoints_resize() + { + int newsize = d_main_gui->getNPoints(); + set_nsamps(newsize); + } + + void + const_sink_c_impl::_gui_update_trigger() + { + d_trigger_mode = d_main_gui->getTriggerMode(); + d_trigger_slope = d_main_gui->getTriggerSlope(); + d_trigger_level = d_main_gui->getTriggerLevel(); + d_trigger_channel = d_main_gui->getTriggerChannel(); + d_trigger_count = 0; + + std::string tagkey = d_main_gui->getTriggerTagKey(); + d_trigger_tag_key = pmt::intern(tagkey); + } + + void + const_sink_c_impl::_test_trigger_tags(int nitems) + { + int trigger_index; + + uint64_t nr = nitems_read(d_trigger_channel); + std::vector<gr::tag_t> tags; + get_tags_in_range(tags, d_trigger_channel, + nr, nr + nitems, + d_trigger_tag_key); + if(tags.size() > 0) { + d_triggered = true; + trigger_index = tags[0].offset - nr; + d_start = d_index + trigger_index - 1; + d_end = d_start + d_size; + d_trigger_count = 0; + } + } + + void + const_sink_c_impl::_test_trigger_norm(int nitems, gr_vector_const_void_star inputs) + { + int trigger_index; + const gr_complex *in = (const gr_complex*)inputs[d_trigger_channel]; + for(trigger_index = 0; trigger_index < nitems; trigger_index++) { + d_trigger_count++; + + // Test if trigger has occurred based on the input stream, + // channel number, and slope direction + if(_test_trigger_slope(&in[trigger_index])) { + d_triggered = true; + d_start = d_index + trigger_index; + d_end = d_start + d_size; + d_trigger_count = 0; + break; + } + } + + // If using auto trigger mode, trigger periodically even + // without a trigger event. + if((d_trigger_mode == TRIG_MODE_AUTO) && (d_trigger_count > d_size)) { + d_triggered = true; + d_trigger_count = 0; + } + } + + bool + const_sink_c_impl::_test_trigger_slope(const gr_complex *in) const + { + float x0, x1; + x0 = abs(in[0]); + x1 = abs(in[1]); + + if(d_trigger_slope == TRIG_SLOPE_POS) + return ((x0 <= d_trigger_level) && (x1 > d_trigger_level)); + else + return ((x0 >= d_trigger_level) && (x1 < d_trigger_level)); } int @@ -332,54 +454,64 @@ namespace gr { gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - int n=0, j=0, idx=0; - const gr_complex *in = (const gr_complex*)input_items[idx]; - - npoints_resize(); - - for(int i=0; i < noutput_items; i+=d_size) { - unsigned int datasize = noutput_items - i; - unsigned int resid = d_size-d_index; - idx = 0; - - // If we have enough input for one full plot, do it - if(datasize >= resid) { - - // Fill up residbufs with d_size number of items - for(n = 0; n < d_nconnections; n++) { - in = (const gr_complex*)input_items[idx++]; - volk_32fc_deinterleave_64f_x2_u(&d_residbufs_real[n][d_index], - &d_residbufs_imag[n][d_index], - &in[j], resid); - } - - // Update the plot if its time - if(gr::high_res_timer_now() - d_last_time > d_update_time) { - d_last_time = gr::high_res_timer_now(); - d_qApplication->postEvent(d_main_gui, - new ConstUpdateEvent(d_residbufs_real, - d_residbufs_imag, - d_size)); - } - - d_index = 0; - j += resid; - } + int n=0; + const gr_complex *in; + + _npoints_resize(); + _gui_update_trigger(); + + int nfill = d_end - d_index; // how much room left in buffers + int nitems = std::min(noutput_items, nfill); // num items we can put in buffers + + // If auto, normal, or tag trigger, look for the trigger + if((d_trigger_mode != TRIG_MODE_FREE) && !d_triggered) { + // trigger off a tag key (first one found) + if(d_trigger_mode == TRIG_MODE_TAG) { + _test_trigger_tags(nitems); + } + // Normal or Auto trigger + else { + _test_trigger_norm(nitems, input_items); + } + } - // Otherwise, copy what we received into the residbufs for next time - else { - for(n = 0; n < d_nconnections; n++) { - in = (const gr_complex*)input_items[idx++]; - volk_32fc_deinterleave_64f_x2_u(&d_residbufs_real[n][d_index], - &d_residbufs_imag[n][d_index], - &in[j], datasize); - } - d_index += datasize; - j += datasize; - } + // Copy data into the buffers. + for(n = 0; n < d_nconnections; n++) { + in = (const gr_complex*)input_items[n]; + volk_32fc_deinterleave_64f_x2(&d_residbufs_real[n][d_index], + &d_residbufs_imag[n][d_index], + &in[0], nitems); + } + d_index += nitems; + + + // If we've have a trigger and a full d_size of items in the buffers, plot. + if((d_triggered) && (d_index == d_end)) { + // Copy data to be plotted to start of buffers. + for(n = 0; n < d_nconnections; n++) { + memmove(d_residbufs_real[n], &d_residbufs_real[n][d_start], d_size*sizeof(double)); + memmove(d_residbufs_imag[n], &d_residbufs_imag[n][d_start], d_size*sizeof(double)); + } + + // Plot if we are able to update + if(gr::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gr::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new ConstUpdateEvent(d_residbufs_real, + d_residbufs_imag, + d_size)); + } + + // We've plotting, so reset the state + _reset(); + } + + // If we've filled up the buffers but haven't triggered, reset. + if(d_index == d_end) { + _reset(); } - return j; + return nitems; } } /* namespace qtgui */ diff --git a/gr-qtgui/lib/const_sink_c_impl.h b/gr-qtgui/lib/const_sink_c_impl.h index 3b1de226ff..0f1540de7e 100644 --- a/gr-qtgui/lib/const_sink_c_impl.h +++ b/gr-qtgui/lib/const_sink_c_impl.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -30,7 +30,7 @@ namespace gr { namespace qtgui { - + class QTGUI_API const_sink_c_impl : public const_sink_c { private: @@ -38,11 +38,11 @@ namespace gr { gr::thread::mutex d_mutex; - int d_size; + int d_size, d_buffer_size; std::string d_name; int d_nconnections; - int d_index; + int d_index, d_start, d_end; std::vector<double*> d_residbufs_real; std::vector<double*> d_residbufs_imag; @@ -54,7 +54,21 @@ namespace gr { gr::high_res_timer_type d_update_time; gr::high_res_timer_type d_last_time; - void npoints_resize(); + // Members used for triggering scope + trigger_mode d_trigger_mode; + trigger_slope d_trigger_slope; + float d_trigger_level; + int d_trigger_channel; + pmt::pmt_t d_trigger_tag_key; + bool d_triggered; + int d_trigger_count; + + void _reset(); + void _npoints_resize(); + void _gui_update_trigger(); + void _test_trigger_tags(int nitems); + void _test_trigger_norm(int nitems, gr_vector_const_void_star inputs); + bool _test_trigger_slope(const gr_complex *in) const; public: const_sink_c_impl(int size, @@ -86,6 +100,9 @@ namespace gr { void set_line_marker(int which, int marker); void set_nsamps(const int size); void set_line_alpha(int which, double alpha); + void set_trigger_mode(trigger_mode mode, trigger_slope slope, + float level, int channel, + const std::string &tag_key=""); std::string title(); std::string line_label(int which); diff --git a/gr-qtgui/lib/constellationdisplayform.cc b/gr-qtgui/lib/constellationdisplayform.cc index d0424b0511..1325beccae 100644 --- a/gr-qtgui/lib/constellationdisplayform.cc +++ b/gr-qtgui/lib/constellationdisplayform.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Free Software Foundation, Inc. + * Copyright 2012,2014 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -36,11 +36,48 @@ ConstellationDisplayForm::ConstellationDisplayForm(int nplots, QWidget* parent) d_layout->addWidget(d_display_plot, 0, 0); setLayout(d_layout); - NPointsMenu *nptsmenu = new NPointsMenu(this); - d_menu->addAction(nptsmenu); - connect(nptsmenu, SIGNAL(whichTrigger(int)), + d_nptsmenu = new NPointsMenu(this); + d_menu->addAction(d_nptsmenu); + connect(d_nptsmenu, SIGNAL(whichTrigger(int)), this, SLOT(setNPoints(const int))); + // Set up the trigger menu + d_triggermenu = new QMenu("Trigger", this); + d_tr_mode_menu = new TriggerModeMenu(this); + d_tr_slope_menu = new TriggerSlopeMenu(this); + d_tr_level_act = new PopupMenu("Level", this); + d_tr_channel_menu = new TriggerChannelMenu(nplots, this); + d_tr_tag_key_act = new PopupMenu("Tag Key", this); + d_triggermenu->addMenu(d_tr_mode_menu); + d_triggermenu->addMenu(d_tr_slope_menu); + d_triggermenu->addAction(d_tr_level_act); + d_triggermenu->addMenu(d_tr_channel_menu); + d_triggermenu->addAction(d_tr_tag_key_act); + d_menu->addMenu(d_triggermenu); + + setTriggerMode(gr::qtgui::TRIG_MODE_FREE); + connect(d_tr_mode_menu, SIGNAL(whichTrigger(gr::qtgui::trigger_mode)), + this, SLOT(setTriggerMode(gr::qtgui::trigger_mode))); + // updates trigger state by calling set level or set tag key. + connect(d_tr_mode_menu, SIGNAL(whichTrigger(gr::qtgui::trigger_mode)), + this, SLOT(updateTrigger(gr::qtgui::trigger_mode))); + + setTriggerSlope(gr::qtgui::TRIG_SLOPE_POS); + connect(d_tr_slope_menu, SIGNAL(whichTrigger(gr::qtgui::trigger_slope)), + this, SLOT(setTriggerSlope(gr::qtgui::trigger_slope))); + + setTriggerLevel(0); + connect(d_tr_level_act, SIGNAL(whichTrigger(QString)), + this, SLOT(setTriggerLevel(QString))); + + setTriggerChannel(0); + connect(d_tr_channel_menu, SIGNAL(whichTrigger(int)), + this, SLOT(setTriggerChannel(int))); + + setTriggerTagKey(std::string("")); + connect(d_tr_tag_key_act, SIGNAL(whichTrigger(QString)), + this, SLOT(setTriggerTagKey(QString))); + Reset(); connect(d_display_plot, SIGNAL(plotPointSelected(const QPointF)), @@ -120,3 +157,96 @@ void ConstellationDisplayForm::setSampleRate(const QString &samprate) { } + +/******************************************************************** + * TRIGGER METHODS + *******************************************************************/ + +void +ConstellationDisplayForm::setTriggerMode(gr::qtgui::trigger_mode mode) +{ + d_trig_mode = mode; + d_tr_mode_menu->getAction(mode)->setChecked(true); +} + +void +ConstellationDisplayForm::updateTrigger(gr::qtgui::trigger_mode mode) +{ + // If auto or normal mode, popup trigger level box to set + if((d_trig_mode == gr::qtgui::TRIG_MODE_AUTO) || (d_trig_mode == gr::qtgui::TRIG_MODE_NORM)) + d_tr_level_act->activate(QAction::Trigger); + + // if tag mode, popup tag key box to set + if(d_trig_mode == gr::qtgui::TRIG_MODE_TAG) + d_tr_tag_key_act->activate(QAction::Trigger); +} + +gr::qtgui::trigger_mode +ConstellationDisplayForm::getTriggerMode() const +{ + return d_trig_mode; +} + +void +ConstellationDisplayForm::setTriggerSlope(gr::qtgui::trigger_slope slope) +{ + d_trig_slope = slope; + d_tr_slope_menu->getAction(slope)->setChecked(true); +} + +gr::qtgui::trigger_slope +ConstellationDisplayForm::getTriggerSlope() const +{ + return d_trig_slope; +} + +void +ConstellationDisplayForm::setTriggerLevel(QString s) +{ + d_trig_level = s.toFloat(); +} + +void +ConstellationDisplayForm::setTriggerLevel(float level) +{ + d_trig_level = level; + d_tr_level_act->setText(QString().setNum(d_trig_level)); +} + +float +ConstellationDisplayForm::getTriggerLevel() const +{ + return d_trig_level; +} + +void +ConstellationDisplayForm::setTriggerChannel(int channel) +{ + d_trig_channel = channel; + d_tr_channel_menu->getAction(d_trig_channel)->setChecked(true); +} + +int +ConstellationDisplayForm::getTriggerChannel() const +{ + return d_trig_channel; +} + +void +ConstellationDisplayForm::setTriggerTagKey(QString s) +{ + d_trig_tag_key = s.toStdString(); +} + +void +ConstellationDisplayForm::setTriggerTagKey(const std::string &key) +{ + d_trig_tag_key = key; + d_tr_tag_key_act->setText(QString().fromStdString(d_trig_tag_key)); +} + +std::string +ConstellationDisplayForm::getTriggerTagKey() const +{ + return d_trig_tag_key; +} diff --git a/gr-qtgui/lib/time_sink_c_impl.cc b/gr-qtgui/lib/time_sink_c_impl.cc index e86ffca227..499529c57d 100644 --- a/gr-qtgui/lib/time_sink_c_impl.cc +++ b/gr-qtgui/lib/time_sink_c_impl.cc @@ -67,7 +67,7 @@ namespace gr { for(int n = 0; n < d_nconnections; n++) { d_buffers.push_back((double*)volk_malloc(d_buffer_size*sizeof(double), - volk_get_alignment())); + volk_get_alignment())); memset(d_buffers[n], 0, d_buffer_size*sizeof(double)); } |