diff options
author | Tom Rondeau <tom@trondeau.com> | 2014-07-14 15:10:19 -0400 |
---|---|---|
committer | Tom Rondeau <tom@trondeau.com> | 2014-07-14 15:10:19 -0400 |
commit | 1fb307d852a5e5ca156a2f85a8a9e87f4c0820af (patch) | |
tree | bdf32806637bf6373a13d32d9cbd1324c121f702 /gr-uhd | |
parent | d83f0377a7c1e3e3af33d24a6cad5eeb4b7e8ba0 (diff) | |
parent | 12523cdb3218be85565c3c3bcb49d113e6bac518 (diff) |
Merge remote-tracking branch 'mbant/uhd/msg_format' into qt_the_things
Diffstat (limited to 'gr-uhd')
-rw-r--r-- | gr-uhd/doc/uhd.dox | 56 | ||||
-rw-r--r-- | gr-uhd/examples/grc/uhd_msg_tune.grc | 1132 | ||||
-rwxr-xr-x | gr-uhd/examples/python/freq_hopping.py | 6 | ||||
-rw-r--r-- | gr-uhd/grc/gen_uhd_usrp_blocks.py | 5 | ||||
-rw-r--r-- | gr-uhd/include/gnuradio/uhd/usrp_sink.h | 46 | ||||
-rw-r--r-- | gr-uhd/include/gnuradio/uhd/usrp_source.h | 42 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_common.h | 198 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_sink_impl.cc | 237 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_sink_impl.h | 26 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_source_impl.cc | 94 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_source_impl.h | 31 |
11 files changed, 1669 insertions, 204 deletions
diff --git a/gr-uhd/doc/uhd.dox b/gr-uhd/doc/uhd.dox index 538c98c438..7a71b240b4 100644 --- a/gr-uhd/doc/uhd.dox +++ b/gr-uhd/doc/uhd.dox @@ -1,4 +1,3 @@ -// vim: set ft=doxygen: /*! \page page_uhd UHD Interface \section Introduction @@ -22,15 +21,61 @@ by using: \endcode -\section External Documentation +\section uhd_external_docs External Documentation -Ettus Research keeps the comprehensive documentation to the underlying UHD driver, which can be found: +Ettus Research maintains the comprehensive documentation to the underlying UHD driver, which can be found at: - http://files.ettus.com/uhd_docs/manual/html/ +http://files.ettus.com/uhd_docs/manual/html/ The UHD Doxygen page is located at: - http://files.ettus.com/uhd_docs/doxygen/html/index.html +http://files.ettus.com/uhd_docs/doxygen/html/index.html + + +\section uhd_command_syntax Command Syntax + +The UHD sink and source can be controlled by a message port. These message ports +take commands, which are PMTs formatted as such: + + (command, value, [channel]) + +A command PMT is either a pair or a tuple. If it's a tuple, it must have either 2 or 3 elements. +Any other type of PMT will throw an error. + +The `command` part is a string, which defines the command. `value` is a PMT whose format depends +on the command issued. Finally, `channel` is an integer PMT value that specifies which channel +this command shall be specified on. If this value is omitted, then it either applies this command +to all channels or channel zero, depending on which command is used. + +Example: +\code{.cpp} +pmt::pmt_t command = pmt::cons( // We make a pair, but pmt::make_tuple() is also valid! + pmt::mp("freq"), // Use the 'freq' command, which sets the frequency + pmt::mp(1.1e9) // Set the frequency to 1.1 GHz +); +\endcode + +This PMT would set the frequency to 1.1 GHz on all channels. We make use of the pmt::mp() function +which automatically sets the PMT types. Assume we only want to set the frequency on channel 1 +(i.e. the second channel). In this case, we must construct a tuple: +\code{.cpp} +pmt::pmt_t command = pmt::make_tuple( + pmt::mp("freq"), // Use the 'freq' command, which sets the frequency + pmt::mp(1.1e9) // Set the frequency to 1.1 GHz + pmt::mp(1) // Select channel 1 +); +\endcode + + +\subsection uhd_command_syntax_cmds Common commands + +The following commands are understood by both UHD Source and Sink: + +Command name | Value Type | Description +-------------|------------|------------------------------------------------------------- +`freq` | double | Sets the Tx or Rx frequency. Defaults to all channels. +`lo_offset` | double | Sets an LO offset. Defaults to all channels. +`gain` | double | Sets the Tx or Rx gain (in dB). Defaults to all channels. \section Configuring a UHD object @@ -100,3 +145,4 @@ resampler to take care of the difference. \endcode */ +// vim: set ft=doxygen: diff --git a/gr-uhd/examples/grc/uhd_msg_tune.grc b/gr-uhd/examples/grc/uhd_msg_tune.grc new file mode 100644 index 0000000000..89022278be --- /dev/null +++ b/gr-uhd/examples/grc/uhd_msg_tune.grc @@ -0,0 +1,1132 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Tue Jul 8 12:08:19 2014</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>uhd_tune_msg</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value>UHD Message Tuner</value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value>Tune a UHD source from a QT sink via messages.</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>prompt</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>alias</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(-1, -2)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>initial_fc</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>100e6</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(-2, 172)</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>2e6</value> + </param> + <param> + <key>alias</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1, 102)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>gain</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value></value> + </param> + <param> + <key>value</key> + <value>20</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>60</value> + </param> + <param> + <key>step</key> + <value>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>_coordinate</key> + <value>(103, 99)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>uhd_usrp_source</key> + <param> + <key>id</key> + <value>uhd_usrp_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fc32</value> + </param> + <param> + <key>otw</key> + <value></value> + </param> + <param> + <key>stream_args</key> + <value></value> + </param> + <param> + <key>stream_chans</key> + <value>[]</value> + </param> + <param> + <key>dev_addr</key> + <value>""</value> + </param> + <param> + <key>dev_args</key> + <value>""</value> + </param> + <param> + <key>sync</key> + <value></value> + </param> + <param> + <key>clock_rate</key> + <value>0.0</value> + </param> + <param> + <key>num_mboards</key> + <value>1</value> + </param> + <param> + <key>clock_source0</key> + <value></value> + </param> + <param> + <key>time_source0</key> + <value></value> + </param> + <param> + <key>sd_spec0</key> + <value></value> + </param> + <param> + <key>clock_source1</key> + <value></value> + </param> + <param> + <key>time_source1</key> + <value></value> + </param> + <param> + <key>sd_spec1</key> + <value></value> + </param> + <param> + <key>clock_source2</key> + <value></value> + </param> + <param> + <key>time_source2</key> + <value></value> + </param> + <param> + <key>sd_spec2</key> + <value></value> + </param> + <param> + <key>clock_source3</key> + <value></value> + </param> + <param> + <key>time_source3</key> + <value></value> + </param> + <param> + <key>sd_spec3</key> + <value></value> + </param> + <param> + <key>clock_source4</key> + <value></value> + </param> + <param> + <key>time_source4</key> + <value></value> + </param> + <param> + <key>sd_spec4</key> + <value></value> + </param> + <param> + <key>clock_source5</key> + <value></value> + </param> + <param> + <key>time_source5</key> + <value></value> + </param> + <param> + <key>sd_spec5</key> + <value></value> + </param> + <param> + <key>clock_source6</key> + <value></value> + </param> + <param> + <key>time_source6</key> + <value></value> + </param> + <param> + <key>sd_spec6</key> + <value></value> + </param> + <param> + <key>clock_source7</key> + <value></value> + </param> + <param> + <key>time_source7</key> + <value></value> + </param> + <param> + <key>sd_spec7</key> + <value></value> + </param> + <param> + <key>nchan</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>center_freq0</key> + <value>initial_fc</value> + </param> + <param> + <key>gain0</key> + <value>gain</value> + </param> + <param> + <key>ant0</key> + <value></value> + </param> + <param> + <key>bw0</key> + <value>0</value> + </param> + <param> + <key>center_freq1</key> + <value>0</value> + </param> + <param> + <key>gain1</key> + <value>0</value> + </param> + <param> + <key>ant1</key> + <value></value> + </param> + <param> + <key>bw1</key> + <value>0</value> + </param> + <param> + <key>center_freq2</key> + <value>0</value> + </param> + <param> + <key>gain2</key> + <value>0</value> + </param> + <param> + <key>ant2</key> + <value></value> + </param> + <param> + <key>bw2</key> + <value>0</value> + </param> + <param> + <key>center_freq3</key> + <value>0</value> + </param> + <param> + <key>gain3</key> + <value>0</value> + </param> + <param> + <key>ant3</key> + <value></value> + </param> + <param> + <key>bw3</key> + <value>0</value> + </param> + <param> + <key>center_freq4</key> + <value>0</value> + </param> + <param> + <key>gain4</key> + <value>0</value> + </param> + <param> + <key>ant4</key> + <value></value> + </param> + <param> + <key>bw4</key> + <value>0</value> + </param> + <param> + <key>center_freq5</key> + <value>0</value> + </param> + <param> + <key>gain5</key> + <value>0</value> + </param> + <param> + <key>ant5</key> + <value></value> + </param> + <param> + <key>bw5</key> + <value>0</value> + </param> + <param> + <key>center_freq6</key> + <value>0</value> + </param> + <param> + <key>gain6</key> + <value>0</value> + </param> + <param> + <key>ant6</key> + <value></value> + </param> + <param> + <key>bw6</key> + <value>0</value> + </param> + <param> + <key>center_freq7</key> + <value>0</value> + </param> + <param> + <key>gain7</key> + <value>0</value> + </param> + <param> + <key>ant7</key> + <value></value> + </param> + <param> + <key>bw7</key> + <value>0</value> + </param> + <param> + <key>center_freq8</key> + <value>0</value> + </param> + <param> + <key>gain8</key> + <value>0</value> + </param> + <param> + <key>ant8</key> + <value></value> + </param> + <param> + <key>bw8</key> + <value>0</value> + </param> + <param> + <key>center_freq9</key> + <value>0</value> + </param> + <param> + <key>gain9</key> + <value>0</value> + </param> + <param> + <key>ant9</key> + <value></value> + </param> + <param> + <key>bw9</key> + <value>0</value> + </param> + <param> + <key>center_freq10</key> + <value>0</value> + </param> + <param> + <key>gain10</key> + <value>0</value> + </param> + <param> + <key>ant10</key> + <value></value> + </param> + <param> + <key>bw10</key> + <value>0</value> + </param> + <param> + <key>center_freq11</key> + <value>0</value> + </param> + <param> + <key>gain11</key> + <value>0</value> + </param> + <param> + <key>ant11</key> + <value></value> + </param> + <param> + <key>bw11</key> + <value>0</value> + </param> + <param> + <key>center_freq12</key> + <value>0</value> + </param> + <param> + <key>gain12</key> + <value>0</value> + </param> + <param> + <key>ant12</key> + <value></value> + </param> + <param> + <key>bw12</key> + <value>0</value> + </param> + <param> + <key>center_freq13</key> + <value>0</value> + </param> + <param> + <key>gain13</key> + <value>0</value> + </param> + <param> + <key>ant13</key> + <value></value> + </param> + <param> + <key>bw13</key> + <value>0</value> + </param> + <param> + <key>center_freq14</key> + <value>0</value> + </param> + <param> + <key>gain14</key> + <value>0</value> + </param> + <param> + <key>ant14</key> + <value></value> + </param> + <param> + <key>bw14</key> + <value>0</value> + </param> + <param> + <key>center_freq15</key> + <value>0</value> + </param> + <param> + <key>gain15</key> + <value>0</value> + </param> + <param> + <key>ant15</key> + <value></value> + </param> + <param> + <key>bw15</key> + <value>0</value> + </param> + <param> + <key>center_freq16</key> + <value>0</value> + </param> + <param> + <key>gain16</key> + <value>0</value> + </param> + <param> + <key>ant16</key> + <value></value> + </param> + <param> + <key>bw16</key> + <value>0</value> + </param> + <param> + <key>center_freq17</key> + <value>0</value> + </param> + <param> + <key>gain17</key> + <value>0</value> + </param> + <param> + <key>ant17</key> + <value></value> + </param> + <param> + <key>bw17</key> + <value>0</value> + </param> + <param> + <key>center_freq18</key> + <value>0</value> + </param> + <param> + <key>gain18</key> + <value>0</value> + </param> + <param> + <key>ant18</key> + <value></value> + </param> + <param> + <key>bw18</key> + <value>0</value> + </param> + <param> + <key>center_freq19</key> + <value>0</value> + </param> + <param> + <key>gain19</key> + <value>0</value> + </param> + <param> + <key>ant19</key> + <value></value> + </param> + <param> + <key>bw19</key> + <value>0</value> + </param> + <param> + <key>center_freq20</key> + <value>0</value> + </param> + <param> + <key>gain20</key> + <value>0</value> + </param> + <param> + <key>ant20</key> + <value></value> + </param> + <param> + <key>bw20</key> + <value>0</value> + </param> + <param> + <key>center_freq21</key> + <value>0</value> + </param> + <param> + <key>gain21</key> + <value>0</value> + </param> + <param> + <key>ant21</key> + <value></value> + </param> + <param> + <key>bw21</key> + <value>0</value> + </param> + <param> + <key>center_freq22</key> + <value>0</value> + </param> + <param> + <key>gain22</key> + <value>0</value> + </param> + <param> + <key>ant22</key> + <value></value> + </param> + <param> + <key>bw22</key> + <value>0</value> + </param> + <param> + <key>center_freq23</key> + <value>0</value> + </param> + <param> + <key>gain23</key> + <value>0</value> + </param> + <param> + <key>ant23</key> + <value></value> + </param> + <param> + <key>bw23</key> + <value>0</value> + </param> + <param> + <key>center_freq24</key> + <value>0</value> + </param> + <param> + <key>gain24</key> + <value>0</value> + </param> + <param> + <key>ant24</key> + <value></value> + </param> + <param> + <key>bw24</key> + <value>0</value> + </param> + <param> + <key>center_freq25</key> + <value>0</value> + </param> + <param> + <key>gain25</key> + <value>0</value> + </param> + <param> + <key>ant25</key> + <value></value> + </param> + <param> + <key>bw25</key> + <value>0</value> + </param> + <param> + <key>center_freq26</key> + <value>0</value> + </param> + <param> + <key>gain26</key> + <value>0</value> + </param> + <param> + <key>ant26</key> + <value></value> + </param> + <param> + <key>bw26</key> + <value>0</value> + </param> + <param> + <key>center_freq27</key> + <value>0</value> + </param> + <param> + <key>gain27</key> + <value>0</value> + </param> + <param> + <key>ant27</key> + <value></value> + </param> + <param> + <key>bw27</key> + <value>0</value> + </param> + <param> + <key>center_freq28</key> + <value>0</value> + </param> + <param> + <key>gain28</key> + <value>0</value> + </param> + <param> + <key>ant28</key> + <value></value> + </param> + <param> + <key>bw28</key> + <value>0</value> + </param> + <param> + <key>center_freq29</key> + <value>0</value> + </param> + <param> + <key>gain29</key> + <value>0</value> + </param> + <param> + <key>ant29</key> + <value></value> + </param> + <param> + <key>bw29</key> + <value>0</value> + </param> + <param> + <key>center_freq30</key> + <value>0</value> + </param> + <param> + <key>gain30</key> + <value>0</value> + </param> + <param> + <key>ant30</key> + <value></value> + </param> + <param> + <key>bw30</key> + <value>0</value> + </param> + <param> + <key>center_freq31</key> + <value>0</value> + </param> + <param> + <key>gain31</key> + <value>0</value> + </param> + <param> + <key>ant31</key> + <value></value> + </param> + <param> + <key>bw31</key> + <value>0</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>_coordinate</key> + <value>(336, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_freq_sink_x</key> + <param> + <key>id</key> + <value>qtgui_freq_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>fftsize</key> + <value>1024</value> + </param> + <param> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> + </param> + <param> + <key>fc</key> + <value>initial_fc</value> + </param> + <param> + <key>bw</key> + <value>samp_rate</value> + </param> + <param> + <key>autoscale</key> + <value>False</value> + </param> + <param> + <key>average</key> + <value>1.0</value> + </param> + <param> + <key>ymin</key> + <value>-140</value> + </param> + <param> + <key>ymax</key> + <value>10</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>label1</key> + <value></value> + </param> + <param> + <key>width1</key> + <value>1</value> + </param> + <param> + <key>color1</key> + <value>"blue"</value> + </param> + <param> + <key>alpha1</key> + <value>1.0</value> + </param> + <param> + <key>label2</key> + <value></value> + </param> + <param> + <key>width2</key> + <value>1</value> + </param> + <param> + <key>color2</key> + <value>"red"</value> + </param> + <param> + <key>alpha2</key> + <value>1.0</value> + </param> + <param> + <key>label3</key> + <value></value> + </param> + <param> + <key>width3</key> + <value>1</value> + </param> + <param> + <key>color3</key> + <value>"green"</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>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>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>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>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>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>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>"dark blue"</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>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>maxoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(330, 104)</value> + </param> + <param> + <key>_rotation</key> + <value>180</value> + </param> + </block> + <connection> + <source_block_id>uhd_usrp_source_0</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>qtgui_freq_sink_x_0</source_block_id> + <sink_block_id>uhd_usrp_source_0</sink_block_id> + <source_key>freq</source_key> + <sink_key>command</sink_key> + </connection> + <connection> + <source_block_id>qtgui_freq_sink_x_0</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>freq</source_key> + <sink_key>freq</sink_key> + </connection> +</flow_graph> diff --git a/gr-uhd/examples/python/freq_hopping.py b/gr-uhd/examples/python/freq_hopping.py index 903b0b23d4..5da5efa241 100755 --- a/gr-uhd/examples/python/freq_hopping.py +++ b/gr-uhd/examples/python/freq_hopping.py @@ -105,7 +105,9 @@ class FrequencyHopperSrc(gr.hier_block2): gain_tag.key = pmt.string_to_symbol('tx_command') gain_tag.value = pmt.cons( pmt.intern("gain"), - pmt.to_pmt((0, tx_gain)) + # These are both valid: + #pmt.from_double(tx_gain) + pmt.cons(pmt.to_pmt(0), pmt.to_pmt(tx_gain)) ) tag_list = [gain_tag,] for i in xrange(n_bursts): @@ -114,7 +116,7 @@ class FrequencyHopperSrc(gr.hier_block2): if i > 0 and post_tuning: tune_tag.offset -= 1 # Move it to last sample of previous burst tune_tag.key = pmt.string_to_symbol('tx_freq') - tune_tag.value = pmt.to_pmt((0, self.hop_sequence[i])) + tune_tag.value = pmt.to_pmt(self.hop_sequence[i]) tag_list.append(tune_tag) length_tag = gr.tag_t() length_tag.offset = i * burst_length diff --git a/gr-uhd/grc/gen_uhd_usrp_blocks.py b/gr-uhd/grc/gen_uhd_usrp_blocks.py index 6490723589..c31ee1325e 100644 --- a/gr-uhd/grc/gen_uhd_usrp_blocks.py +++ b/gr-uhd/grc/gen_uhd_usrp_blocks.py @@ -312,6 +312,11 @@ self.\$(id).set_bandwidth(\$bw$(n), $n) <check>\$num_mboards > 0</check> <check>\$nchan >= \$num_mboards</check> <check>(not \$stream_chans()) or (\$nchan == len(\$stream_chans))</check> + <sink> + <name>command</name> + <type>message</type> + <optional>1</optional> + </sink> <$sourk> <name>$direction</name> <type>\$type.type</type> diff --git a/gr-uhd/include/gnuradio/uhd/usrp_sink.h b/gr-uhd/include/gnuradio/uhd/usrp_sink.h index d3e732cc30..ce8b18ba44 100644 --- a/gr-uhd/include/gnuradio/uhd/usrp_sink.h +++ b/gr-uhd/include/gnuradio/uhd/usrp_sink.h @@ -62,18 +62,17 @@ namespace gr { /*! * \brief DEPRECATED Make a new USRP sink block using the deprecated io_type_t. + * \ingroup uhd_blk * * This function will be removed in the future. Please use the other make function, - * gr::uhd::make(const ::uhd::device_addr_t, const ::uhd::stream_args_t, const std::string). - * - * \ingroup uhd_blk + * gr::uhd::usrp_sink::make(const ::uhd::device_addr_t, const ::uhd::stream_args_t, const std::string). */ static sptr make(const ::uhd::device_addr_t &device_addr, const ::uhd::io_type_t &io_type, size_t num_channels); /*! - * \brief Make a new USRP sink block. + * \brief Make a new USRP sink block (usually a radio transmitter). * * The USRP sink block reads a stream and transmits the samples. * The sink block also provides API calls for transmitter settings. @@ -86,60 +85,55 @@ namespace gr { * - pmt::string_to_symbol("tx_time") * - pmt::string_to_symbol("tx_freq") * - pmt::string_to_symbol("tx_command") - * - pmt::string_to_symbol(length_tag_name) + * - pmt::string_to_symbol(tsb_tag_name) * * Any other tag will be ignored. * * The sob and eob (start and end of burst) tag values are pmt booleans. * When present, burst tags should be set to true (pmt::PMT_T). * - * If length_tag_name is not an empty string, all "tx_sob" and "tx_eob" + * If `tsb_tag_name` is not an empty string, all "tx_sob" and "tx_eob" * tags will be ignored, and the input is assumed to a tagged stream, * as described in \ref page_tagged_stream_blocks. - * The length tag value should be a PMT long specifying the number - * of samples contained in the corresponding tagged stream. * * If sob/eob tags or length tags are used, this block understands that * the data is bursty, and will configure the USRP to make sure there's - * no underruns etc. + * no underruns after transmitting the final sample of a burst. * * The timestamp tag value is a PMT tuple of the following: - * (uint64 seconds, and double fractional seconds). + * (uint64 seconds, double fractional seconds). * - * The tx_freq tag has to be a double, and will issue a tune command to the USRP + * The tx_freq tag has to be a double or a pair of form (channel, frequency), + * with frequency being a double and channel being an integer. + * This tag will trigger a tune command to the USRP * to the given frequency, if possible. Note that oscillators need some time * to stabilize after this! Don't expect clean data to be sent immediately after this command. + * If channel is omitted, and only a double is given, it will set this frequency to all + * channels. * * The command tag can carry a PMT command. See the following section. * - * \section uhd_commands Command interface + * \section uhd_tx_commands Command interface * * There are two ways of passing commands to this block: - * 1) tx_command tag. The command is attached to a sample, and will executed + * 1. tx_command tag. The command is attached to a sample, and will executed * before the sample is transmitted, and after the previous sample. - * 2) The 'command' message port. The command is executed asynchronously, + * 2. The 'command' message port. The command is executed asynchronously, * as soon as possible. * - * In both cases, the payload of the command is a PMT pair, with the first - * item ('car') being the command name, and second ('cdr') the command value. - * - * Command name | Command value - * -------------|-------------------------------------------------------------------------- - * `freq` | Tuple: (chan, new_freq). Requests a tune to `new_freq` on channel `chan`. - * `lo_offset` | Tuple: (chan, lo_offset). Adds a LO offset on channel `chan`. - * `gain` | Tuple: (chan, gain). Requests a gain change to `gain` on channel `chan`. + * In both cases, the payload of the command is a PMT command, as described + * in Section \ref uhd_command_syntax. * - * See the UHD manual for more detailed documentation: - * http://code.ettus.com/redmine/ettus/projects/uhd/wiki + * For a more general description of the gr-uhd components, see \ref page_uhd. * * \param device_addr the address to identify the hardware * \param stream_args the IO format and channel specification - * \param length_tag_name the name of the tag identifying tagged stream length + * \param tsb_tag_name the name of the tag identifying tagged stream length * \return a new USRP sink block object */ static sptr make(const ::uhd::device_addr_t &device_addr, const ::uhd::stream_args_t &stream_args, - const std::string &length_tag_name = ""); + const std::string &tsb_tag_name = ""); /*! * Set the start time for outgoing samples. diff --git a/gr-uhd/include/gnuradio/uhd/usrp_source.h b/gr-uhd/include/gnuradio/uhd/usrp_source.h index 2e165cf771..75e8f8b844 100644 --- a/gr-uhd/include/gnuradio/uhd/usrp_source.h +++ b/gr-uhd/include/gnuradio/uhd/usrp_source.h @@ -27,6 +27,7 @@ #include <gnuradio/sync_block.h> #include <uhd/usrp/multi_usrp.hpp> +// TODO In 3.8, UHD 3.4 will be required and we can remove all these ifdefs #ifndef INCLUDED_UHD_STREAM_HPP namespace uhd { struct GR_UHD_API stream_args_t @@ -60,41 +61,18 @@ namespace gr { typedef boost::shared_ptr<usrp_source> sptr; /*! - * \brief Make a new USRP source block. + * \brief DEPRECATED Make a new USRP source block using the deprecated io_type_t. * \ingroup uhd_blk * - * The USRP source block receives samples and writes to a stream. - * The source block also provides API calls for receiver settings. - * - * RX Stream tagging: - * - * The following tag keys will be produced by the work function: - * - pmt::string_to_symbol("rx_time") - * - pmt::string_to_symbol("rx_rate") - * - pmt::string_to_symbol("rx_freq") - * - * The timstamp tag value is a pmt tuple of the following: - * (uint64 seconds, and double fractional seconds). - * A timestamp tag is produced at start() and after overflows. - * - * The sample rate and center frequency tags are doubles, - * representing the sample rate in Sps and frequency in Hz. - * These tags are produced upon the user changing parameters. - * - * See the UHD manual for more detailed documentation: - * http://code.ettus.com/redmine/ettus/projects/uhd/wiki - * - * \param device_addr the address to identify the hardware - * \param io_type the desired output data type - * \param num_channels number of stream from the device - * \return a new USRP source block object + * This function will be removed in the future. Please use the other make function, + * gr::uhd::make(const ::uhd::device_addr_t, const ::uhd::stream_args_t, const std::string). */ static sptr make(const ::uhd::device_addr_t &device_addr, const ::uhd::io_type_t &io_type, size_t num_channels); /*! - * \brief Make a new USRP source block. + * \brief Make a new USRP source block (usually a radio receiver). * * The USRP source block receives samples and writes to a stream. * The source block also provides API calls for receiver settings. @@ -104,12 +82,16 @@ namespace gr { * The following tag keys will be produced by the work function: * - pmt::string_to_symbol("rx_time") * - * The timstamp tag value is a pmt tuple of the following: + * The timestamp tag value is a pmt tuple of the following: * (uint64 seconds, and double fractional seconds). * A timestamp tag is produced at start() and after overflows. * - * See the UHD manual for more detailed documentation: - * http://code.ettus.com/redmine/ettus/projects/uhd/wiki + * \section uhd_rx_command_iface Command interface + * + * This block has a message port, which consumes UHD PMT commands. + * For a description of the command syntax, see Section \ref uhd_command_syntax. + * + * For a more general description of the gr-uhd components, see \ref page_uhd. * * \param device_addr the address to identify the hardware * \param stream_args the IO format and channel specification diff --git a/gr-uhd/lib/usrp_common.h b/gr-uhd/lib/usrp_common.h new file mode 100644 index 0000000000..29caa585cf --- /dev/null +++ b/gr-uhd/lib/usrp_common.h @@ -0,0 +1,198 @@ +/* -*- c++ -*- */ +/* + * Copyright 2014 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_UHD_USRP_COMMON_H +#define INCLUDED_GR_UHD_USRP_COMMON_H + +#include <pmt/pmt.h> +#include <boost/dynamic_bitset.hpp> +#include <boost/make_shared.hpp> +#include <boost/bind.hpp> +#include <boost/thread.hpp> +#include <uhd/usrp/multi_usrp.hpp> +#include <uhd/convert.hpp> +#include <iostream> + +namespace gr { + namespace uhd { + typedef boost::function< ::uhd::sensor_value_t (const std::string&)> get_sensor_fn_t; + + static const double LOCK_TIMEOUT = 1.5; // s + + //! Helper function for msg_handler_command: + // - Extracts command and the command value from the command PMT + // - Returns true if the command PMT is well formed + // - If a channel is given, return that as well, otherwise set the channel to -1 + static bool _unpack_chan_command( + std::string &command, + pmt::pmt_t &cmd_val, + int &chan, + const pmt::pmt_t &cmd_pmt + ) { + try { + chan = -1; // Default value + if (pmt::is_tuple(cmd_pmt) and (pmt::length(cmd_pmt) == 2 or pmt::length(cmd_pmt) == 3)) { + command = pmt::symbol_to_string(pmt::tuple_ref(cmd_pmt, 0)); + cmd_val = pmt::tuple_ref(cmd_pmt, 1); + if (pmt::length(cmd_pmt) == 3) { + chan = pmt::to_long(pmt::tuple_ref(cmd_pmt, 2)); + } + } + else if (pmt::is_pair(cmd_pmt)) { + command = pmt::symbol_to_string(pmt::car(cmd_pmt)); + cmd_val = pmt::cdr(cmd_pmt); + if (pmt::is_pair(cmd_val)) { + chan = pmt::to_long(pmt::car(cmd_val)); + cmd_val = pmt::cdr(cmd_val); + } + } + else { + return false; + } + } catch (pmt::wrong_type w) { + return false; + } + return true; + } + + //! Helper function for msg_handler_command: + // - Sets a value in vector_to_update to cmd_val, depending on chan + // - If chan is a positive integer, it will set vector_to_update[chan] + // - If chan is -1, it depends on minus_one_updates_all: + // - Either set vector_to_update[0] or + // - Set *all* entries in vector_to_update + // - Returns a dynamic_bitset, all indexes that where changed in + // vector_to_update are set to 1 + template <typename T> + static boost::dynamic_bitset<> _update_vector_from_cmd_val( + std::vector<T> &vector_to_update, + int chan, + const T cmd_val, + bool minus_one_updates_all = false + ) { + boost::dynamic_bitset<> vals_updated(vector_to_update.size()); + if (chan == -1) { + if (minus_one_updates_all) { + for (size_t i = 0; i < vector_to_update.size(); i++) { + if (vector_to_update[i] != cmd_val) { + vals_updated[i] = true; + vector_to_update[i] = cmd_val; + } + } + return vals_updated; + } + chan = 0; + } + if (vector_to_update[chan] != cmd_val) { + vector_to_update[chan] = cmd_val; + vals_updated[chan] = true; + } + + return vals_updated; + } + + + /*! \brief Components common to USRP sink and source. + * + * \param device_addr Device address + options + * \param stream_args Stream args (cpu format, otw format...) + * \param ts_tag_name If this block produces or consumes stream tags, enter the corresponding tag name here + */ + class usrp_common_impl + { + public: + usrp_common_impl( + const ::uhd::device_addr_t &device_addr, + const ::uhd::stream_args_t &stream_args, + const std::string &ts_tag_name + ) : + _stream_args(stream_args), + _nchan(stream_args.channels.size()), + _stream_now(_nchan == 1 and ts_tag_name.empty()), + _start_time_set(false) + { + if(stream_args.cpu_format == "fc32") + _type = boost::make_shared< ::uhd::io_type_t >(::uhd::io_type_t::COMPLEX_FLOAT32); + if(stream_args.cpu_format == "sc16") + _type = boost::make_shared< ::uhd::io_type_t >(::uhd::io_type_t::COMPLEX_INT16); + _dev = ::uhd::usrp::multi_usrp::make(device_addr); + }; + + ~usrp_common_impl() {}; + + protected: + /*! \brief Wait until a timeout or a sensor returns 'locked'. + * + * If a given sensor is not found, this still returns 'true', so we don't throw + * errors or warnings if a sensor wasn't implemented. + */ + bool _wait_for_locked_sensor( + std::vector<std::string> sensor_names, + const std::string &sensor_name, + get_sensor_fn_t get_sensor_fn + ){ + if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end()) + return true; + + boost::system_time start = boost::get_system_time(); + boost::system_time first_lock_time; + + while (true) { + if ((not first_lock_time.is_not_a_date_time()) and + (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(LOCK_TIMEOUT)))) { + break; + } + + if (get_sensor_fn(sensor_name).to_bool()) { + if (first_lock_time.is_not_a_date_time()) + first_lock_time = boost::get_system_time(); + } + else { + first_lock_time = boost::system_time(); //reset to 'not a date time' + + if (boost::get_system_time() > (start + boost::posix_time::seconds(LOCK_TIMEOUT))){ + return false; + } + } + + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + + return true; + } + + //! Shared pointer to the underlying multi_usrp object + ::uhd::usrp::multi_usrp::sptr _dev; + const ::uhd::stream_args_t _stream_args; + boost::shared_ptr< ::uhd::io_type_t > _type; + //! Number of channels (i.e. number of in- or outputs) + size_t _nchan; + bool _stream_now; + ::uhd::time_spec_t _start_time; + bool _start_time_set; + }; + + } /* namespace uhd */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_UHD_USRP_COMMON_H */ + diff --git a/gr-uhd/lib/usrp_sink_impl.cc b/gr-uhd/lib/usrp_sink_impl.cc index b6a98391be..cdbe7b58f4 100644 --- a/gr-uhd/lib/usrp_sink_impl.cc +++ b/gr-uhd/lib/usrp_sink_impl.cc @@ -25,11 +25,12 @@ #include "usrp_sink_impl.h" #include "gr_uhd_common.h" #include <gnuradio/io_signature.h> -#include <boost/make_shared.hpp> namespace gr { namespace uhd { + static const size_t ALL_CHANS = ::uhd::usrp::multi_usrp::ALL_CHANS; + usrp_sink::sptr usrp_sink::make(const ::uhd::device_addr_t &device_addr, const ::uhd::io_type_t &io_type, @@ -66,36 +67,63 @@ namespace gr { : sync_block("gr uhd usrp sink", args_to_io_sig(stream_args), io_signature::make(0, 0, 0)), - _stream_args(stream_args), - _nchan(stream_args.channels.size()), - _stream_now(_nchan == 1 and length_tag_name.empty()), - _start_time_set(false), + usrp_common_impl(device_addr, stream_args, length_tag_name), _length_tag_key(length_tag_name.empty() ? pmt::PMT_NIL : pmt::string_to_symbol(length_tag_name)), _nitems_to_send(0), - _curr_freq(stream_args.channels.size(), 0.0), - _curr_lo_offset(stream_args.channels.size(), 0.0), - _curr_gain(stream_args.channels.size(), 0.0), - _chans_to_tune(stream_args.channels.size(), false), - _call_tune(false) - { - if(stream_args.cpu_format == "fc32") - _type = boost::make_shared< ::uhd::io_type_t >(::uhd::io_type_t::COMPLEX_FLOAT32); - if(stream_args.cpu_format == "sc16") - _type = boost::make_shared< ::uhd::io_type_t >(::uhd::io_type_t::COMPLEX_INT16); - _dev = ::uhd::usrp::multi_usrp::make(device_addr); - + _curr_freq(stream_args.channels.size(), 0.0), + _curr_lo_offset(stream_args.channels.size(), 0.0), + _curr_gain(stream_args.channels.size(), 0.0), + _chans_to_tune(stream_args.channels.size()) + { message_port_register_in(pmt::mp("command")); set_msg_handler( - pmt::mp("command"), - boost::bind(&usrp_sink_impl::msg_handler_command, this, _1) + pmt::mp("command"), + boost::bind(&usrp_sink_impl::msg_handler_command, this, _1) ); - //message_port_register_in(pmt::mp("query")); - //set_msg_handler( - //pmt::mp("query"), - //boost::bind(&usrp_sink_impl::msg_handler_query, this, _1) - //); + + _check_sensors_locked(); + } + + bool usrp_sink_impl::_check_sensors_locked() + { + bool clocks_locked = true; + + // 1) Check ref lock for all mboards + for (size_t mboard_index = 0; mboard_index < _dev->get_num_mboards(); mboard_index++) { + std::string sensor_name = "ref_locked"; + if (_dev->get_clock_source(mboard_index) == "internal") { + continue; + } + else if (_dev->get_clock_source(mboard_index) == "mimo") { + sensor_name = "mimo_locked"; + } + if (not _wait_for_locked_sensor( + get_mboard_sensor_names(mboard_index), + sensor_name, + boost::bind(&usrp_sink_impl::get_mboard_sensor, this, _1, mboard_index) + )) { + GR_LOG_WARN(d_logger, boost::format("Sensor '%s' failed to lock within timeout on motherboard %d.") % sensor_name % mboard_index); + clocks_locked = false; + } + } + + // 2) Check LO for all channels + for (size_t i = 0; i < _nchan; i++) { + size_t chan_index = _stream_args.channels[i]; + if (not _wait_for_locked_sensor( + get_sensor_names(chan_index), + "lo_locked", + boost::bind(&usrp_sink_impl::get_sensor, this, _1, chan_index) + )) { + GR_LOG_WARN(d_logger, boost::format("Sensor 'lo_locked' failed to lock within timeout on channel %d.") % chan_index); + clocks_locked = false; + } + } + + return clocks_locked; } + usrp_sink_impl::~usrp_sink_impl() { } @@ -569,9 +597,12 @@ namespace gr { _metadata.time_spec += ::uhd::time_spec_t(0, num_sent, _sample_rate); // Some post-processing tasks if we actually transmitted the entire burst - if (_call_tune and num_sent == size_t(ninput_items)) { - _set_center_freq_from_internals_allchans(); - _call_tune = false; + if (not _pending_cmds.empty() and num_sent == size_t(ninput_items)) { + GR_LOG_DEBUG(d_debug_logger, boost::format("Executing %d pending commands.") % _pending_cmds.size()); + BOOST_FOREACH(const pmt::pmt_t &cmd_pmt, _pending_cmds) { + msg_handler_command(cmd_pmt); + } + _pending_cmds.clear(); } return num_sent; @@ -593,10 +624,9 @@ namespace gr { // Go through tag list until something indicates the end of a burst. bool found_time_tag = false; bool found_eob = false; - bool found_freq_tag_in_burst = false; - uint64_t freq_cmd_offset = 0; - double freq_cmd_freq; - int freq_cmd_chan; + // For commands that are in the middle in the burst: + std::vector<pmt::pmt_t> commands_in_burst; // Store the command + uint64_t in_burst_cmd_offset = 0; // Store its position BOOST_FOREACH(const tag_t &my_tag, _tags) { const uint64_t my_tag_count = my_tag.offset; const pmt::pmt_t &key = my_tag.key; @@ -605,11 +635,11 @@ namespace gr { if (my_tag_count >= max_count) { break; } - else if (not pmt::is_null(_length_tag_key) and my_tag_count > samp0_count + _nitems_to_send) { + else if (not pmt::is_null(_length_tag_key) and my_tag_count > samp0_count + _nitems_to_send) { break; - } + } - /* I. Bursts that can only be on the first sample of burst + /* I. Tags that can only be on the first sample of a burst * * This includes: * - tx_time @@ -634,7 +664,7 @@ namespace gr { max_count = my_tag_count; break; } - found_time_tag = true; + found_time_tag = true; _metadata.has_time_spec = true; _metadata.time_spec = ::uhd::time_spec_t (pmt::to_uint64(pmt::tuple_ref(value, 0)), @@ -647,8 +677,8 @@ namespace gr { max_count = my_tag_count; break; } - // Bursty tx will not use time specs, unless a tx_time tag is also given. - _metadata.has_time_spec = false; + // Bursty tx will not use time specs, unless a tx_time tag is also given. + _metadata.has_time_spec = false; _metadata.start_of_burst = pmt::to_bool(value); } @@ -656,49 +686,41 @@ namespace gr { else if(not pmt::is_null(_length_tag_key) and pmt::equal(key, _length_tag_key)) { if (my_tag_count != samp0_count) { max_count = my_tag_count; - break; + break; } //If there are still items left to send, the current burst has been preempted. //Set the items remaining counter to the new burst length. Notify the user of //the tag preemption. - else if(_nitems_to_send > 0) { + else if(_nitems_to_send > 0) { std::cerr << "tP" << std::flush; } _nitems_to_send = pmt::to_long(value); _metadata.start_of_burst = true; } - /* II. Bursts that can be on the first OR last sample of a burst + /* II. Tags that can be on the first OR last sample of a burst * * This includes: - * - tx_freq (tags that don't actually change the frequency are ignored) + * - tx_freq * * With these tags, we check if they're at the start of a burst, and do * the appropriate action. Otherwise, make sure the corresponding sample * is the last one. */ - else if (pmt::equal(key, FREQ_KEY) and my_tag_count == samp0_count) { - int chan = pmt::to_long(pmt::tuple_ref(value, 0)); - double new_freq = pmt::to_double(pmt::tuple_ref(value, 1)); - if (new_freq != _curr_freq[chan]) { - _curr_freq[chan] = new_freq; - _set_center_freq_from_internals(chan); - } - } - - else if(pmt::equal(key, FREQ_KEY) and not found_freq_tag_in_burst) { - int chan = pmt::to_long(pmt::tuple_ref(value, 0)); - double new_freq = pmt::to_double(pmt::tuple_ref(value, 1)); - if (new_freq != _curr_freq[chan]) { - freq_cmd_freq = new_freq; - freq_cmd_chan = chan; - freq_cmd_offset = my_tag_count; - max_count = my_tag_count + 1; - found_freq_tag_in_burst = true; - } + else if (pmt::equal(key, FREQ_KEY) and my_tag_count == samp0_count) { + // If it's on the first sample, immediately do the tune: + GR_LOG_DEBUG(d_debug_logger, boost::format("Received tx_freq on start of burst.")); + msg_handler_command(pmt::cons(pmt::mp("freq"), value)); + } + else if(pmt::equal(key, FREQ_KEY)) { + // If it's not on the first sample, queue this command and only tx until here: + GR_LOG_DEBUG(d_debug_logger, boost::format("Received tx_freq mid-burst.")); + commands_in_burst.push_back(pmt::cons(pmt::mp("freq"), value)); + max_count = my_tag_count + 1; + in_burst_cmd_offset = my_tag_count; } - /* III. Bursts that can only be on the last sample of a burst + /* III. Tags that can only be on the last sample of a burst * * This includes: * - tx_eob @@ -716,20 +738,25 @@ namespace gr { found_eob = true; } - if (found_freq_tag_in_burst) { + // If a command was found in-burst that may appear at the end of burst, + // there's two options: + // 1) The command was actually on the last sample (eob). Then, stash the + // commands for running after work(). + // 2) The command was not on the last sample. In this case, only send() + // until before the tag, so it will be on the first sample of the next run. + if (not commands_in_burst.empty()) { if (not found_eob) { // If it's in the middle of a burst, only send() until before the tag - max_count = freq_cmd_offset; - } else if (freq_cmd_offset < max_count) { - // Otherwise, tune after work() - _curr_freq[freq_cmd_chan] = freq_cmd_freq; - _chans_to_tune[freq_cmd_chan] = true; - _call_tune = true; + max_count = in_burst_cmd_offset; + } else if (in_burst_cmd_offset < max_count) { + BOOST_FOREACH(const pmt::pmt_t &cmd_pmt, commands_in_burst) { + _pending_cmds.push_back(cmd_pmt); + } } } if (found_time_tag) { - _metadata.has_time_spec = true; + _metadata.has_time_spec = true; } // Only transmit up to and including end of burst, @@ -801,46 +828,44 @@ namespace gr { /************** External interfaces (RPC + Message passing) ********************/ - // Helper function for msg_handler_command: Extracts chan and command value from - // the 2-tuple in cmd_val, updates the value in vector_to_update[chan] and returns - // true if it was different from the old value. - bool _unpack_chan_command(pmt::pmt_t &cmd_val, int &chan, std::vector<double> &vector_to_update) - { - chan = pmt::to_long(pmt::tuple_ref(cmd_val, 0)); - double new_value = pmt::to_double(pmt::tuple_ref(cmd_val, 1)); - if (new_value == vector_to_update[chan]) { - return false; - } else { - vector_to_update[chan] = new_value; - return true; - } - } - void usrp_sink_impl::msg_handler_command(pmt::pmt_t msg) { - const std::string command(pmt::symbol_to_string(pmt::car(msg))); - pmt::pmt_t value(pmt::cdr(msg)); - int chan = 0; - if (command == "freq") { - if (_unpack_chan_command(value, chan, _curr_freq)) { - _set_center_freq_from_internals(chan); - } - } else if (command == "lo_offset") { - if (_unpack_chan_command(value, chan, _curr_lo_offset)) { - _set_center_freq_from_internals(chan); - } - } else if (command == "gain") { - if (_unpack_chan_command(value, chan, _curr_gain)) { - set_gain(_curr_gain[chan], chan); - } - } else { - GR_LOG_ALERT(d_logger, boost::format("Received unknown command: %s") % command); + std::string command; + pmt::pmt_t cmd_value; + int chan = -1; + if (not _unpack_chan_command(command, cmd_value, chan, msg)) { + GR_LOG_ALERT(d_logger, boost::format("Error while unpacking command PMT: %s") % msg); + return; + } + GR_LOG_DEBUG(d_debug_logger, boost::format("Received command: %s") % command); + try { + if (command == "freq") { + _chans_to_tune = _update_vector_from_cmd_val<double>( + _curr_freq, chan, pmt::to_double(cmd_value), true + ); + _set_center_freq_from_internals_allchans(); + } else if (command == "lo_offset") { + _chans_to_tune = _update_vector_from_cmd_val<double>( + _curr_lo_offset, chan, pmt::to_double(cmd_value), true + ); + _set_center_freq_from_internals_allchans(); + } else if (command == "gain") { + boost::dynamic_bitset<> chans_to_change = _update_vector_from_cmd_val<double>( + _curr_gain, chan, pmt::to_double(cmd_value), true + ); + if (chans_to_change.any()) { + for (size_t i = 0; i < chans_to_change.size(); i++) { + if (chans_to_change[i]) { + set_gain(_curr_gain[i], i); + } + } + } + } else { + GR_LOG_ALERT(d_logger, boost::format("Received unknown command: %s") % command); + } + } catch (pmt::wrong_type &e) { + GR_LOG_ALERT(d_logger, boost::format("Received command '%s' with invalid command value: %s") % command % cmd_value); } - } - - void usrp_sink_impl::msg_handler_query(pmt::pmt_t msg) - { - //tbi } void diff --git a/gr-uhd/lib/usrp_sink_impl.h b/gr-uhd/lib/usrp_sink_impl.h index 12a25658e3..21bb991b92 100644 --- a/gr-uhd/lib/usrp_sink_impl.h +++ b/gr-uhd/lib/usrp_sink_impl.h @@ -22,6 +22,7 @@ #include <gnuradio/uhd/usrp_sink.h> #include <uhd/convert.hpp> +#include "usrp_common.h" static const pmt::pmt_t SOB_KEY = pmt::string_to_symbol("tx_sob"); static const pmt::pmt_t EOB_KEY = pmt::string_to_symbol("tx_eob"); @@ -51,7 +52,7 @@ namespace gr { /*********************************************************************** * UHD Multi USRP Sink Impl **********************************************************************/ - class usrp_sink_impl : public usrp_sink + class usrp_sink_impl : public usrp_sink, public usrp_common_impl { public: usrp_sink_impl(const ::uhd::device_addr_t &device_addr, @@ -122,34 +123,32 @@ namespace gr { inline void tag_work(int &ninput_items); private: + /*! \brief Run through all 'lock' sensors and make sure they are actually locked. + */ + bool _check_sensors_locked(); + //! Like set_center_freq(), but uses _curr_freq and _curr_lo_offset ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan); //! Calls _set_center_freq_from_internals() on all channels void _set_center_freq_from_internals_allchans(); - //! Receives commands and handles them - void msg_handler_command(pmt::pmt_t msg); - //! Receives queries and posts a response - void msg_handler_query(pmt::pmt_t msg); - ::uhd::usrp::multi_usrp::sptr _dev; - const ::uhd::stream_args_t _stream_args; - boost::shared_ptr< ::uhd::io_type_t > _type; #ifdef GR_UHD_USE_STREAM_API ::uhd::tx_streamer::sptr _tx_stream; #endif - size_t _nchan; - bool _stream_now; ::uhd::tx_metadata_t _metadata; double _sample_rate; - ::uhd::time_spec_t _start_time; - bool _start_time_set; //stream tags related stuff std::vector<tag_t> _tags; const pmt::pmt_t _length_tag_key; long _nitems_to_send; + /****** Command interface related **********/ + //! Stores a list of commands for later execution + std::vector<pmt::pmt_t> _pending_cmds; + //! Receives commands and handles them + void msg_handler_command(pmt::pmt_t msg); //! Stores the last value we told the USRP to tune to for every channel // (this is not necessarily the true value the USRP is currently tuned to!). // We could theoretically ask the device, but during streaming, we want to minimize @@ -159,8 +158,7 @@ namespace gr { std::vector<double> _curr_lo_offset; //! Stores the last gain value we told the USRP to have for every channel. std::vector<double> _curr_gain; - std::vector<bool> _chans_to_tune; - bool _call_tune; + boost::dynamic_bitset<> _chans_to_tune; }; } /* namespace uhd */ diff --git a/gr-uhd/lib/usrp_source_impl.cc b/gr-uhd/lib/usrp_source_impl.cc index e57db13a0b..53038bf202 100644 --- a/gr-uhd/lib/usrp_source_impl.cc +++ b/gr-uhd/lib/usrp_source_impl.cc @@ -20,6 +20,7 @@ * Boston, MA 02110-1301, USA. */ +#include "usrp_common.h" #include "usrp_source_impl.h" #include "gr_uhd_common.h" #include <gnuradio/io_signature.h> @@ -66,20 +67,59 @@ namespace gr { sync_block("gr uhd usrp source", io_signature::make(0, 0, 0), args_to_io_sig(stream_args)), - _stream_args(stream_args), - _nchan(stream_args.channels.size()), - _stream_now(_nchan == 1), - _tag_now(false), - _start_time_set(false) - { - if(stream_args.cpu_format == "fc32") - _type = boost::make_shared< ::uhd::io_type_t >(::uhd::io_type_t::COMPLEX_FLOAT32); - if(stream_args.cpu_format == "sc16") - _type = boost::make_shared< ::uhd::io_type_t >(::uhd::io_type_t::COMPLEX_INT16); + usrp_common_impl(device_addr, stream_args, ""), + _tag_now(false) + { std::stringstream str; str << name() << unique_id(); _id = pmt::string_to_symbol(str.str()); - _dev = ::uhd::usrp::multi_usrp::make(device_addr); + + message_port_register_in(pmt::mp("command")); + set_msg_handler( + pmt::mp("command"), + boost::bind(&usrp_source_impl::msg_handler_command, this, _1) + ); + + _check_sensors_locked(); + } + + bool usrp_source_impl::_check_sensors_locked() + { + bool clocks_locked = true; + + // 1) Check ref lock for all mboards + for (size_t mboard_index = 0; mboard_index < _dev->get_num_mboards(); mboard_index++) { + std::string sensor_name = "ref_locked"; + if (_dev->get_clock_source(mboard_index) == "internal") { + continue; + } + else if (_dev->get_clock_source(mboard_index) == "mimo") { + sensor_name = "mimo_locked"; + } + if (not _wait_for_locked_sensor( + get_mboard_sensor_names(mboard_index), + sensor_name, + boost::bind(&usrp_source_impl::get_mboard_sensor, this, _1, mboard_index) + )) { + GR_LOG_WARN(d_logger, boost::format("Sensor '%s' failed to lock within timeout on motherboard %d.") % sensor_name % mboard_index); + clocks_locked = false; + } + } + + // 2) Check LO for all channels + for (size_t i = 0; i < _nchan; i++) { + size_t chan_index = _stream_args.channels[i]; + if (not _wait_for_locked_sensor( + get_sensor_names(chan_index), + "lo_locked", + boost::bind(&usrp_source_impl::get_sensor, this, _1, chan_index) + )) { + GR_LOG_WARN(d_logger, boost::format("Sensor 'lo_locked' failed to lock within timeout on channel %d.") % chan_index); + clocks_locked = false; + } + } + + return clocks_locked; } usrp_source_impl::~usrp_source_impl() @@ -670,6 +710,38 @@ namespace gr { return num_samps; } + + /************** External interfaces (RPC + Message passing) ********************/ + void usrp_source_impl::msg_handler_command(pmt::pmt_t msg) + { + std::string command; + pmt::pmt_t cmd_value; + int chan = -1; + if (not _unpack_chan_command(command, cmd_value, chan, msg)) { + GR_LOG_ALERT(d_logger, "Error while unpacking command PMT."); + } + if (command == "freq") { + double freq = pmt::to_double(cmd_value); + for (size_t i = 0; i < _nchan; i++) { + if (chan == -1 or chan == int(i)) { + set_center_freq(freq, i); + } + } + // TODO: implement + //} else if (command == "lo_offset") { + //; + } else if (command == "gain") { + double gain = pmt::to_double(cmd_value); + for (size_t i = 0; i < _nchan; i++) { + if (chan == -1 or chan == int(i)) { + set_gain(gain, i); + } + } + } else { + GR_LOG_ALERT(d_logger, boost::format("Received unknown command: %s") % command); + } + } + void usrp_source_impl::setup_rpc() { diff --git a/gr-uhd/lib/usrp_source_impl.h b/gr-uhd/lib/usrp_source_impl.h index 3cfa1aad35..6de4c9c6a3 100644 --- a/gr-uhd/lib/usrp_source_impl.h +++ b/gr-uhd/lib/usrp_source_impl.h @@ -23,6 +23,7 @@ #include <gnuradio/uhd/usrp_source.h> #include <uhd/convert.hpp> #include <boost/thread/mutex.hpp> +#include "usrp_common.h" static const pmt::pmt_t TIME_KEY = pmt::string_to_symbol("rx_time"); static const pmt::pmt_t RATE_KEY = pmt::string_to_symbol("rx_rate"); @@ -50,7 +51,7 @@ namespace gr { /*********************************************************************** * UHD Multi USRP Source Impl **********************************************************************/ - class usrp_source_impl : public usrp_source + class usrp_source_impl : public usrp_source, public usrp_common_impl { public: usrp_source_impl(const ::uhd::device_addr_t &device_addr, @@ -124,27 +125,37 @@ namespace gr { gr_vector_void_star &output_items); private: - ::uhd::usrp::multi_usrp::sptr _dev; - const ::uhd::stream_args_t _stream_args; - boost::shared_ptr< ::uhd::io_type_t > _type; - + /*! \brief Run through all 'lock' sensors and make sure they are actually locked. + */ + bool _check_sensors_locked(); #ifdef GR_UHD_USE_STREAM_API ::uhd::rx_streamer::sptr _rx_stream; size_t _samps_per_packet; #endif - size_t _nchan; - bool _stream_now, _tag_now; + bool _tag_now; ::uhd::rx_metadata_t _metadata; pmt::pmt_t _id; - ::uhd::time_spec_t _start_time; - bool _start_time_set; - //tag shadows double _samp_rate; double _center_freq; boost::recursive_mutex d_mutex; + + /****** Command interface related **********/ + //! Receives commands and handles them + void msg_handler_command(pmt::pmt_t msg); + //! Stores the last value we told the USRP to tune to for every channel + // (this is not necessarily the true value the USRP is currently tuned to!). + // We could theoretically ask the device, but during streaming, we want to minimize + // communication with the USRP. + std::vector<double> _curr_freq; + //! Stores the last value we told the USRP to have the LO offset for every channel. + std::vector<double> _curr_lo_offset; + //! Stores the last gain value we told the USRP to have for every channel. + std::vector<double> _curr_gain; + boost::dynamic_bitset<> _chans_to_tune; + bool _call_tune; }; } /* namespace uhd */ |