diff options
Diffstat (limited to 'gnuradio-core/src')
67 files changed, 7865 insertions, 3 deletions
diff --git a/gnuradio-core/src/examples/CMakeLists.txt b/gnuradio-core/src/examples/CMakeLists.txt index fc06c23476..7e27b123ee 100644 --- a/gnuradio-core/src/examples/CMakeLists.txt +++ b/gnuradio-core/src/examples/CMakeLists.txt @@ -20,4 +20,9 @@ add_subdirectory(mp-sched) add_subdirectory(network) add_subdirectory(tags) -add_subdirectory(volk_benchmark)
\ No newline at end of file +add_subdirectory(volk_benchmark) + +if(ENABLE_GR_CTRLPORT) +add_subdirectory(ctrlport) +endif(ENABLE_GR_CTRLPORT) +
\ No newline at end of file diff --git a/gnuradio-core/src/examples/ctrlport/CMakeLists.txt b/gnuradio-core/src/examples/ctrlport/CMakeLists.txt new file mode 100644 index 0000000000..47ef4c225e --- /dev/null +++ b/gnuradio-core/src/examples/ctrlport/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright 2012 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. + +include(GrPython) + +GR_PYTHON_INSTALL(PROGRAMS + DESTINATION ${GR_PKG_CTRLPORT_EXAMPLES_DIR} + COMPONENT "core_python" +) diff --git a/gnuradio-core/src/examples/ctrlport/pfb_sync_test-qt.grc b/gnuradio-core/src/examples/ctrlport/pfb_sync_test-qt.grc new file mode 100644 index 0000000000..7dffe82c8d --- /dev/null +++ b/gnuradio-core/src/examples/ctrlport/pfb_sync_test-qt.grc @@ -0,0 +1,772 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Fri Nov 30 13:35:55 2012</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>pfb_sync_test_qt</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>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>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>graymap</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>[[3,1,0,2]]</value> + </param> + <param> + <key>_coordinate</key> + <value>(32, 387)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>amps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>[1]</value> + </param> + <param> + <key>_coordinate</key> + <value>(32, 451)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>nfilts</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32</value> + </param> + <param> + <key>_coordinate</key> + <value>(99, 451)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_throttle</key> + <param> + <key>id</key> + <value>gr_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(623, 64)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_file_source</key> + <param> + <key>id</key> + <value>gr_file_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>file</key> + <value>/dev/urandom</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(228, 56)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_packed_to_unpacked_xx</key> + <param> + <key>id</key> + <value>gr_packed_to_unpacked_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>bits_per_chunk</key> + <value>8</value> + </param> + <param> + <key>endianness</key> + <value>gr.GR_MSB_FIRST</value> + </param> + <param> + <key>num_ports</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 56)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>sps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2</value> + </param> + <param> + <key>_coordinate</key> + <value>(105, 126)</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>300000</value> + </param> + <param> + <key>_coordinate</key> + <value>(14, 124)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import random, math, cmath</value> + </param> + <param> + <key>_coordinate</key> + <value>(14, 77)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phase</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Phase</value> + </param> + <param> + <key>value</key> + <value>0.5</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>2</value> + </param> + <param> + <key>step</key> + <value>0.01</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>_coordinate</key> + <value>(175, 387)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_null_sink</key> + <param> + <key>id</key> + <value>gr_null_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(939, 313)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>noise</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Noise</value> + </param> + <param> + <key>value</key> + <value>0.050</value> + </param> + <param> + <key>start</key> + <value>0.0001</value> + </param> + <param> + <key>stop</key> + <value>2</value> + </param> + <param> + <key>step</key> + <value>0.01</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>_coordinate</key> + <value>(316, 389)</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</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>size</key> + <value>1024</value> + </param> + <param> + <key>ymin</key> + <value>-2</value> + </param> + <param> + <key>ymax</key> + <value>2</value> + </param> + <param> + <key>xmin</key> + <value>-2</value> + </param> + <param> + <key>xmax</key> + <value>2</value> + </param> + <param> + <key>nconnections</key> + <value>2</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(949, 232)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>monitor</key> + <param> + <key>id</key> + <value>monitor_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>en</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(229, 6)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_pfb_clock_sync_xxx</key> + <param> + <key>id</key> + <value>digital_pfb_clock_sync_xxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>ccf</value> + </param> + <param> + <key>sps</key> + <value>sps</value> + </param> + <param> + <key>loop_bw</key> + <value>2*3.14/100.0</value> + </param> + <param> + <key>taps</key> + <value>firdes.root_raised_cosine(nfilts, nfilts,1.0/sps, 0.35, int(22*sps*nfilts))</value> + </param> + <param> + <key>filter_size</key> + <value>nfilts</value> + </param> + <param> + <key>init_phase</key> + <value>nfilts/2</value> + </param> + <param> + <key>max_dev</key> + <value>1.5</value> + </param> + <param> + <key>osps</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(322, 231)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_psk_mod</key> + <param> + <key>id</key> + <value>digital_psk_mod_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>constellation_points</key> + <value>4</value> + </param> + <param> + <key>mod_code</key> + <value>"gray"</value> + </param> + <param> + <key>differential</key> + <value>True</value> + </param> + <param> + <key>samples_per_symbol</key> + <value>sps</value> + </param> + <param> + <key>excess_bw</key> + <value>0.35</value> + </param> + <param> + <key>verbose</key> + <value>False</value> + </param> + <param> + <key>log</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(846, 32)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_channel_model</key> + <param> + <key>id</key> + <value>channels_channel_model_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>noise_voltage</key> + <value>noise</value> + </param> + <param> + <key>freq_offset</key> + <value>0.0</value> + </param> + <param> + <key>epsilon</key> + <value>1.0</value> + </param> + <param> + <key>taps</key> + <value>cmath.exp(1j*noise)</value> + </param> + <param> + <key>seed</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(80, 247)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </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>6.28/100.0</value> + </param> + <param> + <key>order</key> + <value>4</value> + </param> + <param> + <key>_coordinate</key> + <value>(641, 313)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_ctrlport_probe2_c</key> + <param> + <key>id</key> + <value>probe2_c_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>name</key> + <value>constellation</value> + </param> + <param> + <key>desc</key> + <value>Constellation Points</value> + </param> + <param> + <key>len</key> + <value>1024</value> + </param> + <param> + <key>_coordinate</key> + <value>(936, 396)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>gr_packed_to_unpacked_xx_0</source_block_id> + <sink_block_id>gr_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>gr_throttle_0</source_block_id> + <sink_block_id>digital_psk_mod_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_pfb_clock_sync_xxx_0</source_block_id> + <sink_block_id>digital_costas_loop_cc_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>gr_file_source_0</source_block_id> + <sink_block_id>gr_packed_to_unpacked_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_pfb_clock_sync_xxx_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> + <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>1</sink_key> + </connection> + <connection> + <source_block_id>digital_costas_loop_cc_0</source_block_id> + <sink_block_id>gr_null_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_channel_model_0</source_block_id> + <sink_block_id>digital_pfb_clock_sync_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_psk_mod_0</source_block_id> + <sink_block_id>channels_channel_model_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>probe2_c_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gnuradio-core/src/examples/ctrlport/pfb_sync_test.grc b/gnuradio-core/src/examples/ctrlport/pfb_sync_test.grc new file mode 100644 index 0000000000..9789086b9a --- /dev/null +++ b/gnuradio-core/src/examples/ctrlport/pfb_sync_test.grc @@ -0,0 +1,705 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Tue Nov 27 16:02:43 2012</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>pfb_sync_test</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value></value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>wx_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>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import random, math, cmath</value> + </param> + <param> + <key>_coordinate</key> + <value>(14, 77)</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>300000</value> + </param> + <param> + <key>_coordinate</key> + <value>(14, 124)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>graymap</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>[[3,1,0,2]]</value> + </param> + <param> + <key>_coordinate</key> + <value>(32, 387)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>amps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>[1]</value> + </param> + <param> + <key>_coordinate</key> + <value>(32, 451)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>nfilts</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32</value> + </param> + <param> + <key>_coordinate</key> + <value>(99, 451)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </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>6.28/100.0</value> + </param> + <param> + <key>order</key> + <value>4</value> + </param> + <param> + <key>_coordinate</key> + <value>(626, 223)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>sps</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>2</value> + </param> + <param> + <key>_coordinate</key> + <value>(105, 126)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_null_sink</key> + <param> + <key>id</key> + <value>gr_null_sink_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(964, 200)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>id</key> + <value>noise</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0.05</value> + </param> + <param> + <key>min</key> + <value>0.00000001</value> + </param> + <param> + <key>max</key> + <value>2</value> + </param> + <param> + <key>num_steps</key> + <value>100</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(281, 406)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>id</key> + <value>phase</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value></value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>max</key> + <value>2</value> + </param> + <param> + <key>num_steps</key> + <value>100</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(167, 405)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_file_source</key> + <param> + <key>id</key> + <value>gr_file_source_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>file</key> + <value>/dev/urandom</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(228, 56)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_packed_to_unpacked_xx</key> + <param> + <key>id</key> + <value>gr_packed_to_unpacked_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>bits_per_chunk</key> + <value>8</value> + </param> + <param> + <key>endianness</key> + <value>gr.GR_MSB_FIRST</value> + </param> + <param> + <key>num_ports</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(408, 56)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>gr_throttle</key> + <param> + <key>id</key> + <value>gr_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(623, 64)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>monitor</key> + <param> + <key>id</key> + <value>monitor_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>en</key> + <value>True</value> + </param> + <param> + <key>_coordinate</key> + <value>(229, 6)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_pfb_clock_sync_xxx</key> + <param> + <key>id</key> + <value>digital_pfb_clock_sync_xxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>ccf</value> + </param> + <param> + <key>sps</key> + <value>sps</value> + </param> + <param> + <key>loop_bw</key> + <value>2*3.14/100.0</value> + </param> + <param> + <key>taps</key> + <value>firdes.root_raised_cosine(nfilts, nfilts,1.0/sps, 0.35, int(22*sps*nfilts))</value> + </param> + <param> + <key>filter_size</key> + <value>nfilts</value> + </param> + <param> + <key>init_phase</key> + <value>nfilts/2</value> + </param> + <param> + <key>max_dev</key> + <value>1.5</value> + </param> + <param> + <key>osps</key> + <value>1</value> + </param> + <param> + <key>_coordinate</key> + <value>(322, 231)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_psk_mod</key> + <param> + <key>id</key> + <value>digital_psk_mod_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>constellation_points</key> + <value>4</value> + </param> + <param> + <key>mod_code</key> + <value>"gray"</value> + </param> + <param> + <key>differential</key> + <value>True</value> + </param> + <param> + <key>samples_per_symbol</key> + <value>sps</value> + </param> + <param> + <key>excess_bw</key> + <value>0.35</value> + </param> + <param> + <key>verbose</key> + <value>False</value> + </param> + <param> + <key>log</key> + <value>False</value> + </param> + <param> + <key>_coordinate</key> + <value>(846, 32)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_channel_model</key> + <param> + <key>id</key> + <value>channels_channel_model_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>noise_voltage</key> + <value>noise</value> + </param> + <param> + <key>freq_offset</key> + <value>0.0</value> + </param> + <param> + <key>epsilon</key> + <value>1.0</value> + </param> + <param> + <key>taps</key> + <value>cmath.exp(1j*phase)</value> + </param> + <param> + <key>seed</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(73, 247)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>probe2_c</key> + <param> + <key>id</key> + <value>probe2_c_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>name</key> + <value>constellation</value> + </param> + <param> + <key>desc</key> + <value>Constellation Points</value> + </param> + <param> + <key>len</key> + <value>1024</value> + </param> + <param> + <key>_coordinate</key> + <value>(912, 243)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>gr_packed_to_unpacked_xx_0</source_block_id> + <sink_block_id>gr_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>gr_file_source_0</source_block_id> + <sink_block_id>gr_packed_to_unpacked_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>gr_throttle_0</source_block_id> + <sink_block_id>digital_psk_mod_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_pfb_clock_sync_xxx_0</source_block_id> + <sink_block_id>digital_costas_loop_cc_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>gr_null_sink_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_channel_model_0</source_block_id> + <sink_block_id>digital_pfb_clock_sync_xxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_psk_mod_0</source_block_id> + <sink_block_id>channels_channel_model_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_channel_model_0</source_block_id> + <sink_block_id>probe2_c_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gnuradio-core/src/lib/CMakeLists.txt b/gnuradio-core/src/lib/CMakeLists.txt index dcb81c36a2..84b4fc131b 100644 --- a/gnuradio-core/src/lib/CMakeLists.txt +++ b/gnuradio-core/src/lib/CMakeLists.txt @@ -69,10 +69,46 @@ endif() # Link against libvolk list(APPEND gnuradio_core_libs volk) -add_library(gnuradio-core SHARED ${gnuradio_core_sources}) +if(ENABLE_GR_CTRLPORT) + +######################################################################## +# Run ICE To compile Slice files +######################################################################## +EXECUTE_PROCESS( + COMMAND "${ICE_SLICE2CPP}" "-I${CMAKE_CURRENT_SOURCE_DIR}/runtime" + "--output-dir=${CMAKE_CURRENT_BINARY_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/runtime/gnuradio.ice" + ) + +# Append generated file in build directory +list(APPEND gnuradio_core_generated_sources + ${CMAKE_CURRENT_BINARY_DIR}/gnuradio.cpp +) + +list(APPEND gnuradio_core_generated_includes + ${CMAKE_CURRENT_BINARY_DIR}/gnuradio.h +) + +######################################################################## +# Add controlport stuff to gnuradio-core +######################################################################## + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +list(APPEND gnuradio_core_libs + ${ICE_LIBRARIES} +) + +endif(ENABLE_GR_CTRLPORT) + +add_library(gnuradio-core SHARED ${gnuradio_core_sources} ${gnuradio_core_generated_sources}) target_link_libraries(gnuradio-core ${gnuradio_core_libs}) GR_LIBRARY_FOO(gnuradio-core RUNTIME_COMPONENT "core_runtime" DEVEL_COMPONENT "core_devel") set_target_properties(gnuradio-core PROPERTIES LINK_INTERFACE_LIBRARIES "gruel") +ADD_DEPENDENCIES(gnuradio-core + gnuradio_core_generated_sources + gnuradio_core_generated_includes + gnuradio_core_generated_swigs) ######################################################################## # Setup executables diff --git a/gnuradio-core/src/lib/general/CMakeLists.txt b/gnuradio-core/src/lib/general/CMakeLists.txt index 80097c9db4..5978a035c7 100644 --- a/gnuradio-core/src/lib/general/CMakeLists.txt +++ b/gnuradio-core/src/lib/general/CMakeLists.txt @@ -242,6 +242,15 @@ set(gr_core_general_triple_threats gr_message_strobe ) +if(ENABLE_GR_CTRLPORT) +ADD_DEFINITIONS(-DGR_CTRLPORT) +list(APPEND gr_core_general_triple_threats + gr_ctrlport_probe_c + gr_ctrlport_probe2_c +) +endif(ENABLE_GR_CTRLPORT) + + foreach(file_tt ${gr_core_general_triple_threats}) list(APPEND gnuradio_core_sources ${CMAKE_CURRENT_SOURCE_DIR}/${file_tt}.cc) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${file_tt}.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio COMPONENT "core_devel") diff --git a/gnuradio-core/src/lib/general/general.i b/gnuradio-core/src/lib/general/general.i index af275686f0..9af804afed 100644 --- a/gnuradio-core/src/lib/general/general.i +++ b/gnuradio-core/src/lib/general/general.i @@ -183,3 +183,16 @@ %include "gr_tag_debug.i" %include "gr_block_gateway.i" %include "gr_message_strobe.i" + + +#ifdef GR_CTRLPORT + +%{ +#include <gr_ctrlport_probe_c.h> +#include <gr_ctrlport_probe2_c.h> +%} + +%include "gr_ctrlport_probe_c.i" +%include "gr_ctrlport_probe2_c.i" + +#endif /* GR_CTRLPORT */ diff --git a/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.cc b/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.cc new file mode 100644 index 0000000000..5d6abd2ee5 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.cc @@ -0,0 +1,139 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_ctrlport_probe2_c.h> +#include <gr_io_signature.h> + +gr_ctrlport_probe2_c_sptr +gr_make_ctrlport_probe2_c(const std::string &id, + const std::string &desc, int len) +{ + return gnuradio::get_initial_sptr + (new gr_ctrlport_probe2_c(id, desc, len)); +} + +gr_ctrlport_probe2_c::gr_ctrlport_probe2_c(const std::string &id, + const std::string &desc, int len) + : gr_sync_block("probe2_c", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_len(len), + d_const_rpc(d_name, id.c_str(), this, unique_id(), &gr_ctrlport_probe2_c::get, + pmt::pmt_make_c32vector(0,-2), + pmt::pmt_make_c32vector(0,2), + pmt::pmt_make_c32vector(0,0), + "volts", desc.c_str(), RPC_PRIVLVL_MIN, DISPXYSCATTER), + d_len_get_rpc(d_name, "length", this, unique_id(), &gr_ctrlport_probe2_c::length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "get vector length", RPC_PRIVLVL_MIN, DISPNULL), + d_len_set_rpc(d_name, "length", this, unique_id(), &gr_ctrlport_probe2_c::set_length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "set vector length", RPC_PRIVLVL_MIN, DISPNULL) +{ + set_length(len); +} + +gr_ctrlport_probe2_c::~gr_ctrlport_probe2_c() +{ +} + +void +gr_ctrlport_probe2_c::forecast(int noutput_items, gr_vector_int &ninput_items_required) +{ + // make sure all inputs have noutput_items available + unsigned ninputs = ninput_items_required.size(); + for(unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = d_len; +} + +// boost::shared_mutex mutex_buffer; +// mutable boost::mutex mutex_notify; +// boost::condition_variable condition_buffer_ready; +std::vector<gr_complex> +gr_ctrlport_probe2_c::get() +{ + mutex_buffer.lock(); + d_buffer.clear(); + mutex_buffer.unlock(); + + // wait for condition + boost::mutex::scoped_lock lock(mutex_notify); + condition_buffer_ready.wait(lock); + + mutex_buffer.lock(); + std::vector<gr_complex> buf_copy = d_buffer; + assert(buf_copy.size() == d_len); + mutex_buffer.unlock(); + + return buf_copy; +} + +void +gr_ctrlport_probe2_c::set_length(int len) +{ + if(len > 8191) { + std::cerr << "probe2_c: length " << len + << " exceeds maximum buffer size of 8191" << std::endl; + len = 8191; + } + + d_len = len; + d_buffer.reserve(d_len); +} + +int +gr_ctrlport_probe2_c::length() const +{ + return (int)d_len; +} + +int +gr_ctrlport_probe2_c::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex*)input_items[0]; + + // copy samples to get buffer if we need samples + mutex_buffer.lock(); + if(d_buffer.size() < d_len) { + // copy smaller of remaining buffer space and num inputs to work() + int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + + // TODO: convert this to a copy operator for speed... + for(int i = 0; i < num_copy; i++) { + d_buffer.push_back(in[i]); + } + + // notify the waiting get() if we fill up the buffer + if(d_buffer.size() == d_len) { + condition_buffer_ready.notify_one(); + } + } + mutex_buffer.unlock(); + + return noutput_items; +} diff --git a/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.h b/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.h new file mode 100644 index 0000000000..aa0f86f635 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.h @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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_CTRLPORT_PROBE2_C_H +#define INCLUDED_CTRLPORT_PROBE2_C_H + +#include <gr_core_api.h> +#include <gr_sync_block.h> +#include <rpcregisterhelpers.h> +#include <boost/thread/shared_mutex.hpp> + +class gr_ctrlport_probe2_c; +typedef boost::shared_ptr<gr_ctrlport_probe2_c> gr_ctrlport_probe2_c_sptr; + +GR_CORE_API gr_ctrlport_probe2_c_sptr +gr_make_ctrlport_probe2_c(const std::string &id, const std::string &desc, int len); + +class GR_CORE_API gr_ctrlport_probe2_c : public gr_sync_block +{ + private: + friend GR_CORE_API gr_ctrlport_probe2_c_sptr gr_make_ctrlport_probe2_c + (const std::string &id, const std::string &desc, int len); + + gr_ctrlport_probe2_c(const std::string &id, const std::string &desc, int len); + + size_t d_len; + boost::shared_mutex mutex_buffer; + mutable boost::mutex mutex_notify; + boost::condition_variable condition_buffer_ready; + + std::vector<gr_complex> d_buffer; + + rpcbasic_register_get<gr_ctrlport_probe2_c, std::vector<std::complex<float> > > d_const_rpc; + rpcbasic_register_get<gr_ctrlport_probe2_c, int> d_len_get_rpc; + rpcbasic_register_set<gr_ctrlport_probe2_c, int> d_len_set_rpc; + + public: + ~gr_ctrlport_probe2_c(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + std::vector<gr_complex> get(); + + void set_length(int len); + int length() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_CTRLPORT_PROBE2_C_H */ + diff --git a/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.i b/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.i new file mode 100644 index 0000000000..18858595ea --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ctrlport_probe2_c.i @@ -0,0 +1,36 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +GR_SWIG_BLOCK_MAGIC(gr,ctrlport_probe2_c) + +gr_ctrlport_probe2_c_sptr +gr_make_ctrlport_probe2_c(const std::string &id, const std::string &desc, int len); + +class gr_ctrlport_probe2_c : public gr_sync_block +{ +public: + ~gr_ctrlport_probe2_c(); + std::vector<gr_complex> get(); + void set_length(int len); + int length() const; +}; + diff --git a/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.cc b/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.cc new file mode 100644 index 0000000000..017a1fdcf7 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.cc @@ -0,0 +1,86 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gr_ctrlport_probe_c.h> +#include <gr_io_signature.h> + +gr_ctrlport_probe_c_sptr +gr_make_ctrlport_probe_c(const std::string &id, + const std::string &desc) +{ + return gnuradio::get_initial_sptr + (new gr_ctrlport_probe_c(id, desc)); +} + + +gr_ctrlport_probe_c::gr_ctrlport_probe_c(const std::string &id, + const std::string &desc) + : gr_sync_block("probe_c", + gr_make_io_signature(1, 1, sizeof(gr_complex)), + gr_make_io_signature(0, 0, 0)), + d_ptr(NULL), d_ptrLen(0), + d_const_rpc(d_name, id.c_str(), this, unique_id(), &gr_ctrlport_probe_c::get, + pmt::pmt_make_c32vector(0,-2), + pmt::pmt_make_c32vector(0,2), + pmt::pmt_make_c32vector(0,0), + "volts", desc.c_str(), RPC_PRIVLVL_MIN, DISPXYSCATTER) +{ +} + +gr_ctrlport_probe_c::~gr_ctrlport_probe_c() +{ +} + +std::vector<gr_complex> +gr_ctrlport_probe_c::get() +{ + if(d_ptr != NULL && d_ptrLen > 0) { + ptrlock.lock(); + std::vector<gr_complex> vec(d_ptr, d_ptr+d_ptrLen); + ptrlock.unlock(); + return vec; + } + else { + std::vector<gr_complex> vec; + return vec; + } +} + +int +gr_ctrlport_probe_c::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + const gr_complex *in = (const gr_complex*)input_items[0]; + + // keep reference to symbols + ptrlock.lock(); + d_ptr = in; + d_ptrLen = noutput_items; + ptrlock.unlock(); + + return noutput_items; +} diff --git a/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.h b/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.h new file mode 100644 index 0000000000..bf9b4ffa4c --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.h @@ -0,0 +1,63 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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_CTRLPORT_PROBE_C_H +#define INCLUDED_CTRLPORT_PROBE_C_H + +#include <gr_core_api.h> +#include <gr_sync_block.h> +#include <rpcregisterhelpers.h> +#include <boost/thread/shared_mutex.hpp> + +class gr_ctrlport_probe_c; +typedef boost::shared_ptr<gr_ctrlport_probe_c> gr_ctrlport_probe_c_sptr; + +GR_CORE_API gr_ctrlport_probe_c_sptr +gr_make_ctrlport_probe_c(const std::string &id, const std::string &desc); + +class GR_CORE_API gr_ctrlport_probe_c : public gr_sync_block +{ + private: + friend GR_CORE_API gr_ctrlport_probe_c_sptr gr_make_ctrlport_probe_c + (const std::string &id, const std::string &desc); + + gr_ctrlport_probe_c(const std::string &id, const std::string &desc); + + boost::shared_mutex ptrlock; + + const gr_complex* d_ptr; + size_t d_ptrLen; + + rpcbasic_register_get<gr_ctrlport_probe_c, std::vector<std::complex<float> > > d_const_rpc; + + public: + ~gr_ctrlport_probe_c(); + + std::vector<gr_complex> get(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* INCLUDED_CTRLPORT_GR_CTRLPORT_PROBE_C_H */ + diff --git a/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.i b/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.i new file mode 100644 index 0000000000..cd4c521cb0 --- /dev/null +++ b/gnuradio-core/src/lib/general/gr_ctrlport_probe_c.i @@ -0,0 +1,34 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +GR_SWIG_BLOCK_MAGIC(gr,ctrlport_probe_c) + +gr_ctrlport_probe_c_sptr +gr_make_ctrlport_probe_c(const std::string &id, const std::string &desc); + +class gr_ctrlport_probe_c : public gr_sync_block +{ +public: + ~gr_ctrlport_probe_c(); + std::vector<gr_complex> get(); +}; + diff --git a/gnuradio-core/src/lib/general/gr_nop.cc b/gnuradio-core/src/lib/general/gr_nop.cc index edfe1d76d9..bfecc861dc 100644 --- a/gnuradio-core/src/lib/general/gr_nop.cc +++ b/gnuradio-core/src/lib/general/gr_nop.cc @@ -27,6 +27,10 @@ #include <gr_io_signature.h> #include <boost/bind.hpp> +#ifdef GR_CTRLPORT +#include <rpcregisterhelpers.h> +#endif + gr_nop_sptr gr_make_nop (size_t sizeof_stream_item) { @@ -39,6 +43,8 @@ gr_nop::gr_nop (size_t sizeof_stream_item) gr_make_io_signature (0, -1, sizeof_stream_item)), d_nmsgs_recvd(0) { + set_rpc(); + // Arrange to have count_received_msgs called when messages are received. message_port_register_in(pmt::mp("port")); set_msg_handler(pmt::mp("port"), boost::bind(&gr_nop::count_received_msgs, this, _1)); @@ -64,3 +70,25 @@ gr_nop::general_work (int noutput_items, return noutput_items; } + +void +gr_nop::set_rpc() +{ +#ifdef GR_CTRLPORT + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<gr_nop, int>( + d_name, "test", this, unique_id(), + &gr_nop::ctrlport_test, + pmt::mp(-256), pmt::mp(255), pmt::mp(0), + "", "Simple testing variable", + RPC_PRIVLVL_MIN, DISPNULL))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_set<gr_nop, int>( + d_name, "test", this, unique_id(), + &gr_nop::set_ctrlport_test, + pmt::mp(-256), pmt::mp(255), pmt::mp(0), + "", "Simple testing variable", + RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ +} diff --git a/gnuradio-core/src/lib/general/gr_nop.h b/gnuradio-core/src/lib/general/gr_nop.h index e0d59280f9..09bd900129 100644 --- a/gnuradio-core/src/lib/general/gr_nop.h +++ b/gnuradio-core/src/lib/general/gr_nop.h @@ -42,8 +42,12 @@ class GR_CORE_API gr_nop : public gr_block friend GR_CORE_API gr_nop_sptr gr_make_nop (size_t sizeof_stream_item); gr_nop (size_t sizeof_stream_item); + std::vector<boost::any> d_rpc_vars; + void set_rpc(); + protected: int d_nmsgs_recvd; + int d_ctrlport_test; // Method that just counts any received messages. void count_received_msgs(pmt::pmt_t msg); @@ -56,6 +60,8 @@ protected: int nmsgs_received() const { return d_nmsgs_recvd; } + int ctrlport_test() { return d_ctrlport_test; } + void set_ctrlport_test(int x) { d_ctrlport_test = x; } }; #endif /* INCLUDED_GR_NOP_H */ diff --git a/gnuradio-core/src/lib/general/gr_nop.i b/gnuradio-core/src/lib/general/gr_nop.i index 977a15d186..73ffa93630 100644 --- a/gnuradio-core/src/lib/general/gr_nop.i +++ b/gnuradio-core/src/lib/general/gr_nop.i @@ -25,6 +25,9 @@ GR_SWIG_BLOCK_MAGIC(gr,nop) gr_nop_sptr gr_make_nop (size_t sizeof_stream_item); class gr_nop : public gr_block { +public: + int ctrlport_test(); + void set_ctrlport_test(int x); private: gr_nop (size_t sizeof_stream_item); }; diff --git a/gnuradio-core/src/lib/runtime/CMakeLists.txt b/gnuradio-core/src/lib/runtime/CMakeLists.txt index 70938a0f17..11bfcfe279 100644 --- a/gnuradio-core/src/lib/runtime/CMakeLists.txt +++ b/gnuradio-core/src/lib/runtime/CMakeLists.txt @@ -90,6 +90,20 @@ list(APPEND gnuradio_core_sources ${CMAKE_CURRENT_SOURCE_DIR}/gr_select_handler.cc ) +if(ENABLE_GR_CTRLPORT) +list(APPEND gnuradio_core_sources + ${CMAKE_CURRENT_SOURCE_DIR}/ice_application_base.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_ice.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_booter_ice.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_booter_aggregator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_aggregator.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_selector.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rpcpmtconverters_ice.cc + ${CMAKE_CURRENT_SOURCE_DIR}/rpcmanager.cc +) +endif(ENABLE_GR_CTRLPORT) + + ######################################################################## # Append gnuradio-core test sources ######################################################################## @@ -157,6 +171,28 @@ install(FILES COMPONENT "core_devel" ) + +if(ENABLE_GR_CTRLPORT) +ADD_DEFINITIONS(-DGR_CTRLPORT) +INSTALL(FILES + ${CMAKE_CURRENT_SOURCE_DIR}/ice_application_base.h + ${CMAKE_CURRENT_SOURCE_DIR}/ice_server_template.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpccallbackregister_base.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcmanager_base.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcmanager.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcpmtconverters_ice.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcregisterhelpers.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_aggregator.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_base.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_booter_aggregator.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_booter_base.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_booter_ice.h + ${CMAKE_CURRENT_SOURCE_DIR}/rpcserver_selector.h + DESTINATION ${GR_INCLUDE_DIR}/gnuradio + COMPONENT "core_devel" +) +endif(ENABLE_GR_CTRLPORT) + ######################################################################## # Install swig headers ######################################################################## diff --git a/gnuradio-core/src/lib/runtime/ICE_LICENSE b/gnuradio-core/src/lib/runtime/ICE_LICENSE new file mode 100644 index 0000000000..43ea7572d9 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/ICE_LICENSE @@ -0,0 +1,54 @@ +Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved. + +This copy of Ice is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +Ice 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 version +2 along with this program; if not, see http://www.gnu.org/licenses. + +Linking Ice statically or dynamically with other software (such as a +library, module or application) is making a combined work based on Ice. +Thus, the terms and conditions of the GNU General Public License version +2 cover this combined work. + +If such software can only be used together with Ice, then not only the +combined work but the software itself is a work derived from Ice and as +such shall be licensed under the terms of the GNU General Public License +version 2. This includes the situation where Ice is only being used +through an abstraction layer. + +As a special exception to the above, ZeroC grants to the contributors for +the following projects the permission to license their Ice-based software +under the terms of the GNU Lesser General Public License (LGPL) version +2.1 or of the BSD license: + + - Orca Robotics (http://orca-robotics.sourceforge.net) + + - Mumble (http://mumble.sourceforge.net) + +This exception does not extend to the parts of Ice used by these +projects, or to any other derived work: as a whole, any work based on Ice +shall be licensed under the terms and conditions of the GNU General +Public License version 2. + +You may also combine Ice with any software not derived from Ice, provided +the license of such software is compatible with the GNU General Public +License version 2. In addition, as a special exception, ZeroC grants you +permission to combine Ice with: + + - the OpenSSL library, or with a modified version of the OpenSSL library + that uses the same license as OpenSSL + + - any library not derived from Ice and licensed under the terms of + the Apache License, version 2.0 + (http://www.apache.org/licenses/LICENSE-2.0.html) + +If you modify this copy of Ice, you may extend any of the exceptions +provided above to your version of Ice, but you are not obligated to +do so. diff --git a/gnuradio-core/src/lib/runtime/IcePy_Communicator.h b/gnuradio-core/src/lib/runtime/IcePy_Communicator.h new file mode 100644 index 0000000000..d613190d2c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/IcePy_Communicator.h @@ -0,0 +1,35 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICEPY_COMMUNICATOR_H +#define ICEPY_COMMUNICATOR_H + +#include <Ice/CommunicatorF.h> +#include <gr_core_api.h> + +namespace IcePy +{ + +extern PyTypeObject CommunicatorType; + +GR_CORE_API bool initCommunicator(PyObject*); + +GR_CORE_API Ice::CommunicatorPtr getCommunicator(PyObject*); + +GR_CORE_API PyObject* createCommunicator(const Ice::CommunicatorPtr&); +GR_CORE_API PyObject* getCommunicatorWrapper(const Ice::CommunicatorPtr&); + +} + +extern "C" PyObject* IcePy_initialize(PyObject*, PyObject*); +extern "C" PyObject* IcePy_initializeWithProperties(PyObject*, PyObject*); +extern "C" PyObject* IcePy_initializeWithLogger(PyObject*, PyObject*); +extern "C" PyObject* IcePy_initializeWithPropertiesAndLogger(PyObject*, PyObject*); + +#endif diff --git a/gnuradio-core/src/lib/runtime/frontend.ice b/gnuradio-core/src/lib/runtime/frontend.ice new file mode 100644 index 0000000000..befb5b7a97 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/frontend.ice @@ -0,0 +1,102 @@ +/* + * Copyright 2012 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. + */ + +#include <gnuradio.ice> + +[["python:package:gnuradio.ctrlport"]] +module GNURadio { + module Frontend { + + exception NotSupported {}; + exception InvalidSetting { string msg; }; + exception ReceiverFailure { string msg; }; + exception NotExist {}; + dictionary<string, string> TunerArgs; + + struct TunerStatus { + int a2dbits; + float gain; + bool isInverted; + }; + + interface Tuner { + TunerStatus configureTuner(TunerArgs args); //ADDED + idempotent TunerStatus status(); + idempotent float setGain(float gain) throws NotSupported, InvalidSetting; + idempotent bool setInversion(bool inverted) throws NotSupported, InvalidSetting; + }; + + struct ChannelStatus { + string uid; + bool active; + float freq; + float bandwidth; + int payloadBits; + bool isComplex; + string signalName; + }; + + interface Channel extends Component { + idempotent ChannelStatus status(); + idempotent FeedInfo feed(); + idempotent bool active(); + void start(); + void stop(); + idempotent float setCenterFreq(float freq) throws NotSupported, InvalidSetting; + idempotent float setBandwidth(float bw) throws NotSupported, InvalidSetting; + idempotent int setPayloadBits(int bits) throws NotSupported, InvalidSetting; + idempotent bool setComplex(bool complex) throws NotSupported, InvalidSetting; + void removeChannel() throws NotSupported; + }; + + sequence<Tuner*> TunerSeq; + sequence<Channel*> ChannelSeq; + + struct ChannelizerStatus { + string uid; + string signalName; + }; + + interface Channelizer extends Component { + idempotent ChannelizerStatus status(); + idempotent Tuner* getTuner(); + idempotent ChannelSeq getChannels(); + idempotent ChannelSeq getActiveChannels(); + idempotent ChannelSeq getInactiveChannels(); + Channel* createChannel(float freq, float bw, int payloadBits, string address, int port) throws NotSupported; + }; + + sequence<Channelizer*> ChannelizerSeq; + + interface Receiver extends AbstractReceiver { + idempotent ChannelizerSeq getInputs(); +// idempotent ChannelizerSeq getActiveInputs(); +// idempotent ChannelizerSeq getInactiveInputs(); + idempotent Channel* getChannelByID(string id) throws NotExist; + idempotent Channelizer* getChannelizerByID(string id) throws NotExist; + }; + + + }; + + + +}; diff --git a/gnuradio-core/src/lib/runtime/gnuradio.ice b/gnuradio-core/src/lib/runtime/gnuradio.ice new file mode 100644 index 0000000000..971d8fa0e6 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gnuradio.ice @@ -0,0 +1,150 @@ +/* + * Copyright 2012 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. + */ + +[["python:package:gnuradio.ctrlport"]] + +#ifndef GNURADIO_DEBUG +#define GNURADIO_DEBUG + +module GNURadio { +class Knob {}; +class KnobB extends Knob { bool value; }; +class KnobC extends Knob { byte value; }; +class KnobI extends Knob { int value; }; +class KnobF extends Knob { float value; }; +class KnobD extends Knob { double value; }; +class KnobL extends Knob { long value; }; +class KnobS extends Knob { string value; }; + +sequence<bool> VectorB; sequence<byte> VectorC; +sequence<int> VectorI; sequence<float> VectorF; +sequence<double> VectorD; sequence<string> VectorS; +sequence<long> VectorL; + +class KnobVecB extends Knob { VectorB value; }; +class KnobVecC extends Knob { VectorC value; }; +class KnobVecI extends Knob { VectorI value; }; +class KnobVecF extends Knob { VectorF value; }; +class KnobVecD extends Knob { VectorD value; }; +class KnobVecL extends Knob { VectorL value; }; +class KnobVecS extends Knob { VectorS value; }; + +enum KnobType { KNOBBOOL, KNOBCHAR, KNOBINT, KNOBFLOAT, + KNOBDOUBLE, KNOBSTRING, KNOBLONG, KNOBVECBOOL, + KNOBVECCHAR, KNOBVECINT, KNOBVECFLOAT, KNOBVECDOUBLE, + KNOBVECSTRING, KNOBVECLONG }; + +enum DisplayType { + DISPNULL, + DISPTIMESERIESF, + DISPTIMESERIESC, + DISPXYSCATTER, + DISPXYLINE +}; + +struct KnobProp { + KnobType type; + string units; + string description; + DisplayType display; + Knob min; + Knob max; + Knob defaultvalue; +}; + +sequence<string> KnobIDList; +dictionary<string, Knob> KnobMap; +dictionary<string, KnobProp> KnobPropMap; +dictionary<string, string> WaveformArgMap; + +interface StreamReceiver { + void push(VectorC data); +}; + +interface ControlPort { + void set(KnobMap knobs); + idempotent KnobMap get(KnobIDList knobs); + idempotent KnobPropMap properties(KnobIDList knobs); + void shutdown(); + +// string subscribe(StreamReceiver* proxy, string streamName, int requestedPeriod, int RequestedSize); +// idempotent void unsubscribe(string streamID); +}; + +struct FeedInfo { + string protocol; + string address; + string iface; + string port; +}; + +//TODO: convert this part to a Feed Info +struct ReceiverInfo { + string uid; + string signalType; + string signalName; + string allocatableObjectID; + string signalProtocol; + string signalAddress; + string signalInterface; + string signalPort; +}; + +interface Component { + void setName(string newName); +}; + +module Frontend { + interface AbstractReceiver extends Component { + idempotent ReceiverInfo getReceiverInfo(); + }; +}; + +module Booter { + dictionary<string, string> WaveformArgs; + + exception WaveformRunningError { + string waveformClass; + float centerFrequencyHz; + }; + exception SignalSourceError {string msg; }; + + interface WaveformBooter extends Frontend::AbstractReceiver { + string launchWaveform(string waveformClass, WaveformArgs args) + throws WaveformRunningError, SignalSourceError; + +// string launchWaveformWithSession(string waveformClass, WaveformArgs args, IceGrid::Session* session) +// throws WaveformRunningError; + WaveformArgMap getDriverEnum(); + WaveformArgMap getSourceInfo(); + idempotent bool waveformRunning(); + idempotent string getWaveformClass(); + void shutdown(); + }; +}; + +//interface Pingable { +// bool ping(); +//}; + +}; + +#endif diff --git a/gnuradio-core/src/lib/runtime/gr_basic_block.h b/gnuradio-core/src/lib/runtime/gr_basic_block.h index e0fd5d2afd..7ad9191b72 100644 --- a/gnuradio-core/src/lib/runtime/gr_basic_block.h +++ b/gnuradio-core/src/lib/runtime/gr_basic_block.h @@ -37,6 +37,10 @@ #include <boost/foreach.hpp> #include <boost/thread/condition_variable.hpp> +#ifdef GR_CTRLPORT +#include <rpcregisterhelpers.h> +#endif + /*! * \brief The abstract base class for all signal processing blocks. * \ingroup internal @@ -95,6 +99,8 @@ protected: std::string d_symbol_alias; vcolor d_color; + std::vector<boost::any> d_rpc_vars; // container for all RPC variables + gr_basic_block(void){} //allows pure virtual interface sub-classes //! Protected constructor prevents instantiation by non-derived classes @@ -229,6 +235,35 @@ public: throw std::runtime_error("attempt to set_msg_handler() on bad input message port!"); } d_msg_handlers[which_port] = msg_handler_t(msg_handler); } + +#ifdef GR_CTRLPORT + /*! + * \brief Add an RPC variable (get or set). + * + * Using controlport, we create new getters/setters and need to + * store them. Each block has a vector to do this, and these never + * need to be accessed again once they are registered with the RPC + * backend. This function takes a + * boost::shared_sptr<rpcbasic_base> so that when the block is + * deleted, all RPC registered variables are cleaned up. + * + * \param s an rpcbasic_sptr of the new RPC variable register to store. + */ + void add_rpc_variable(rpcbasic_sptr s) + { + d_rpc_vars.push_back(s); + } +#endif /* GR_CTRLPORT */ + + /*! + * \brief Set up the RPC registered variables. + * + * This must be overloaded by a block that wants to use + * controlport. This is where rpcbasic_register_{get,set} pointers + * are created, which then get wrapped as shared pointers + * (rpcbasic_sptr(...)) and stored using add_rpc_variable. + */ + virtual void setup_rpc() {}; }; inline bool operator<(gr_basic_block_sptr lhs, gr_basic_block_sptr rhs) diff --git a/gnuradio-core/src/lib/runtime/gr_top_block.cc b/gnuradio-core/src/lib/runtime/gr_top_block.cc index e47473edd8..05cebe7d7f 100644 --- a/gnuradio-core/src/lib/runtime/gr_top_block.cc +++ b/gnuradio-core/src/lib/runtime/gr_top_block.cc @@ -42,6 +42,8 @@ gr_top_block::gr_top_block(const std::string &name) gr_make_io_signature(0,0,0)) { + setup_rpc(); + d_impl = new gr_top_block_impl(this); } @@ -113,3 +115,27 @@ gr_top_block::to_top_block() { return cast_to_top_block_sptr(shared_from_this()); } + +void +gr_top_block::setup_rpc() +{ +#ifdef GR_CTRLPORT + // Getters + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<gr_top_block, int>( + d_name, "max nouptut_items", this, unique_id(), + &gr_top_block::max_noutput_items, + pmt::mp(0), pmt::mp(8192), pmt::mp(8192), + "items", "Max number of output items", + RPC_PRIVLVL_MIN, DISPNULL))); + + // Setters + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_set<gr_top_block, int>( + d_name, "max noutput_items", this, unique_id(), + &gr_top_block::set_max_noutput_items, + pmt::mp(0), pmt::mp(8192), pmt::mp(8192), + "items", "Max number of output items", + RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ +} diff --git a/gnuradio-core/src/lib/runtime/gr_top_block.h b/gnuradio-core/src/lib/runtime/gr_top_block.h index 482a2beb1e..10a21a6434 100644 --- a/gnuradio-core/src/lib/runtime/gr_top_block.h +++ b/gnuradio-core/src/lib/runtime/gr_top_block.h @@ -123,6 +123,8 @@ public: void set_max_noutput_items(int nmax); gr_top_block_sptr to_top_block(); // Needed for Python type coercion + + void setup_rpc(); }; inline gr_top_block_sptr cast_to_top_block_sptr(gr_basic_block_sptr block) { diff --git a/gnuradio-core/src/lib/runtime/ice_application_base.cc b/gnuradio-core/src/lib/runtime/ice_application_base.cc new file mode 100644 index 0000000000..88db6056c1 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/ice_application_base.cc @@ -0,0 +1,43 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <ice_application_base.h> + +int ice_application_common::d_reacquire_attributes(0); +bool ice_application_common::d_main_called(false); +bool ice_application_common::d_have_ice_config(false); +boost::shared_ptr<boost::thread> ice_application_common::d_thread; +std::string ice_application_common::d_endpointStr(""); + +boost::shared_ptr<ice_application_common> +ice_application_common::Instance() +{ + static boost::shared_ptr<ice_application_common> + instance(new ice_application_common()); + return instance; +} + +int ice_application_common::run(int, char**) +{ + communicator()->waitForShutdown(); + return EXIT_SUCCESS; +} diff --git a/gnuradio-core/src/lib/runtime/ice_application_base.h b/gnuradio-core/src/lib/runtime/ice_application_base.h new file mode 100644 index 0000000000..44671f84b4 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/ice_application_base.h @@ -0,0 +1,242 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 ICE_APPLICATION_BASE_H +#define ICE_APPLICATION_BASE_H + +#include <gr_core_api.h> +#include <Ice/Ice.h> +#include <boost/thread.hpp> +#include <boost/thread/mutex.hpp> +#include <stdio.h> +#include <iostream> +#include <set> +#include <string> +#include <stdio.h> + +namespace { + static const unsigned int ICEAPPLICATION_ACTIVATION_TIMEOUT_MS(600); +}; + + +class GR_CORE_API ice_application_common : public Ice::Application +{ + public: + template<typename TserverBase, typename TserverClass> friend class ice_application_base; + static boost::shared_ptr<ice_application_common> Instance(); + ~ice_application_common() {;} + static int d_reacquire_attributes; + + protected: + static bool d_main_called, d_have_ice_config; + static std::string d_endpointStr; + static boost::shared_ptr<boost::thread> d_thread; + ice_application_common() {;} + int run(int, char*[]); +}; + +template<typename TserverBase, typename TserverClass> +class ice_application_base +{ +public: + boost::shared_ptr<ice_application_common> d_application; + ice_application_base(TserverClass* _this); + ~ice_application_base() {;} + + static TserverBase* i(); + static const std::vector<std::string> endpoints(); + +protected: + bool have_ice_config() { return d_application->d_have_ice_config; } + void set_endpoint(const std::string& endpoint) { d_application->d_endpointStr = endpoint;} + + //this one is the key... overwrite in templated/inherited variants + virtual TserverBase* i_impl() = 0; + + //tools for the i_impl... + //tell it when it has to resync with the communicator + virtual bool reacquire_sync(); + virtual void sync_reacquire(); + + static TserverClass* d_this; + + int d_reacquire; + //static int d_reacquire_attributes; + +private: + void starticeexample(); + + bool application_started(); + + int run(int, char*[]); + + static void kickoff(); +}; + +template<typename TserverBase, typename TserverClass> +TserverClass* ice_application_base<TserverBase, TserverClass>::d_this(0); + +//template<typename TserverBase, typename TserverClass> +//int ice_application_base<TserverBase, TserverClass>::d_reacquire_attributes(0); + +template<typename TserverBase, typename TserverClass> +ice_application_base<TserverBase, TserverClass>::ice_application_base(TserverClass* _this) + : d_reacquire(0) +{ + //d_reacquire_attributes = 0; + d_this = _this; + d_application = ice_application_common::Instance(); +} + +template<typename TserverBase, typename TserverClass> +void ice_application_base<TserverBase, TserverClass>::starticeexample() +{ + char derp[] = ""; char* argv[2]; argv[0]=derp; + char buf[1024]; buf[0] = 0; + const char iceconf[] = "--Ice.Config="; + FILE *fp; + + sprintf(buf, "/proc/%d/cmdline", getpid()); + + if(NULL == (fp = fopen(buf, "r"))) { + fprintf(stderr, "Cannot open file %s\n", buf); + exit(EXIT_FAILURE); + } + + unsigned int counter(0); + while(fread(buf, 1, 1, fp)) { + if(*buf == iceconf[counter]) { + if(++counter == sizeof(iceconf) - 1) { + int result = fread(buf, sizeof(buf), 1, fp); + if((result == 0) && (feof(fp) == 0)) { + fprintf(stderr, "ICE file read failur %d\n", ferror(fp)); + clearerr(fp); + exit(EXIT_FAILURE); + } + break; + } + } + } + fclose(fp); + + if(buf[0]) { + ice_application_common::d_have_ice_config = true; + ice_application_common::d_main_called = true; + d_application->main(0, argv, buf); + } + else { + ice_application_common::d_have_ice_config = false; + ice_application_common::d_main_called = true; + d_application->main(0, argv); + } +} + +template<typename TserverBase, typename TserverClass> +void ice_application_base<TserverBase, TserverClass>::kickoff() +{ + static bool run_once = false; + + //if(!d_this->application_started()) { + if(!run_once) { + ++d_this->d_application->d_reacquire_attributes; + + ice_application_common::d_thread = boost::shared_ptr<boost::thread> + (new boost::thread(boost::bind(&ice_application_base::starticeexample, d_this))); + + ::timespec timer_ts, rem_ts; + timer_ts.tv_sec = 0; timer_ts.tv_nsec = ICEAPPLICATION_ACTIVATION_TIMEOUT_MS*1000; + + int iter = 0; + while(!d_this->application_started()) { + ::nanosleep(&timer_ts, &rem_ts); + if(!d_this->application_started()) + std::cout << "@"; + if(iter++ > 100) { + std::cout << "ice_application_base::kickoff(), timeout waiting to get communicator() d_application->main() might have failed?!" << std::endl;; + break; + } + } + + run_once = true; + } + + return; +} + + +template<typename TserverBase, typename TserverClass> +bool ice_application_base<TserverBase, TserverClass>::reacquire_sync() +{ + return (d_this->d_reacquire != d_application->d_reacquire_attributes); +} + +template<typename TserverBase, typename TserverClass> +void ice_application_base<TserverBase, TserverClass>::sync_reacquire() +{ + d_this->d_reacquire = d_application->d_reacquire_attributes; +} + + +template<typename TserverBase, typename TserverClass> +const std::vector<std::string> ice_application_base<TserverBase, TserverClass>::endpoints() +{ + std::vector<std::string> ep; ep.push_back(d_this->d_application->d_endpointStr); return ep; +} + +template<typename TserverBase, typename TserverClass> +TserverBase* ice_application_base<TserverBase, TserverClass>::i() +{ + //printf("indacall\n"); + + assert(d_this != 0); + if(!d_this->application_started()) { + //printf("anotherkickoff\n"); + kickoff(); + } + //printf("donekickedoff\n"); + + /*else if(!d_proxy) { + d_proxy = d_this->i_impl(); + assert(d_proxy != 0); + }*/ + + return d_this->i_impl(); +} + +/*template<typename TserverBase, typename TserverClass> + int ice_application_base<TserverBase, TserverClass>::run(int argc, char* argv[]) { + int implreturn(run_impl(argc, argv)); + ice_application_base<TserverBase, TserverClass>::communicator()->waitForShutdown(); + return implreturn; + }*/ + +template<typename TserverBase, typename TImplClass> +bool ice_application_base<TserverBase, TImplClass>::application_started() +{ + return ice_application_base<TserverBase, TImplClass>::d_this->d_application->communicator(); +} + +/*template<typename TserverBase, typename TImplClass> +int ice_application_base<TserverBase, TImplClass>::run_impl(int argc, char* argv[]) { return EXIT_SUCCESS; } +*/ + +#endif diff --git a/gnuradio-core/src/lib/runtime/ice_server_template.h b/gnuradio-core/src/lib/runtime/ice_server_template.h new file mode 100644 index 0000000000..8ddb03cc8e --- /dev/null +++ b/gnuradio-core/src/lib/runtime/ice_server_template.h @@ -0,0 +1,96 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 ICE_SERVER_TEMPLATE_H +#define ICE_SERVER_TEMPLATE_H + +#include <ice_application_base.h> +#include <iostream> + +template<typename TserverBase, typename TserverClass, typename TImplClass, typename TIceClass> +class ice_server_template : public ice_application_base<TserverBase, TImplClass> +{ +public: + ice_server_template(TImplClass* _this, + const std::string& contolPortName, + const std::string& endpointName); + ~ice_server_template(); + +protected: + //virtual bool application_started(); + TserverBase* i_impl(); + friend class ice_application_base<TserverBase, TImplClass>; + +private: + //virtual int run_impl(int, char*[]); + Ice::ObjectAdapterPtr d_adapter; + TserverBase* d_iceserver; + const std::string d_contolPortName, d_endpointName; +}; + +template<typename TserverBase, typename TserverClass, typename TImplClass, typename TIceClass> +ice_server_template<TserverBase, TserverClass, TImplClass, TIceClass>::ice_server_template + (TImplClass* _this, const std::string& controlPortName, const std::string& endpointName) + : ice_application_base<TserverBase, TImplClass>(_this), + d_iceserver(0), + d_contolPortName(controlPortName), + d_endpointName(endpointName) +{;} + +template<typename TserverBase, typename TserverClass, typename TImplClass, typename TIceClass> +ice_server_template<TserverBase, TserverClass,TImplClass, TIceClass>::~ice_server_template() +{ + if(d_adapter) { + d_adapter->deactivate(); + delete(d_iceserver); + d_adapter = 0; + } +} + +template<typename TserverBase, typename TserverClass, typename TImplClass, typename TIceClass> +TserverBase* ice_server_template<TserverBase, TserverClass, TImplClass, TIceClass>::i_impl() +{ + if(ice_application_base<TserverBase, TImplClass>::d_this->reacquire_sync()) { + d_adapter = (ice_application_base<TserverBase, TImplClass>::d_this->have_ice_config()) ? + ice_application_base<TserverBase, TImplClass>::d_this->d_this->d_application->communicator()->createObjectAdapter(d_contolPortName) : + ice_application_base<TserverBase, TImplClass>::d_this->d_this->d_application->communicator()->createObjectAdapterWithEndpoints(d_contolPortName,"tcp -h *"); + + TserverClass* server_ice(new TserverClass()); + TIceClass obj(server_ice); + + Ice::Identity id(ice_application_base<TserverBase, TImplClass>::d_this->d_this->d_application->communicator()->stringToIdentity(d_endpointName)); + d_adapter->add(obj, id); + d_adapter->activate(); + ice_application_base<TserverBase, TImplClass>::d_this->set_endpoint(ice_application_common::communicator()->proxyToString(d_adapter->createDirectProxy(id))); + + std::cout << std::endl << "Ice Radio Endpoint: " + << ice_server_template<TserverBase, TserverClass, TImplClass, TIceClass>::endpoints()[0] + << std::endl; + + d_iceserver = (TserverBase*) server_ice; + ice_application_base<TserverBase, TImplClass>::d_this->sync_reacquire(); + } + + return d_iceserver; +} + +#endif /* ICE_SERVER_TEMPLATE_H */ diff --git a/gnuradio-core/src/lib/runtime/nop.h b/gnuradio-core/src/lib/runtime/nop.h new file mode 100644 index 0000000000..5b3166da7b --- /dev/null +++ b/gnuradio-core/src/lib/runtime/nop.h @@ -0,0 +1,57 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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_CTRLPORT_NOP_H +#define INCLUDED_CTRLPORT_NOP_H + +#include <ctrlport/api.h> +#include <gr_sync_block.h> + +namespace gr { + namespace ctrlport { + + /*! + * \brief A NOP block for testing ctrlport + * + */ + class CTRLPORT_API nop : virtual public gr_sync_block + { + public: + // gr::ctrlport::nop::sptr + typedef boost::shared_ptr<nop> sptr; + + /*! + * Build a simple test block + */ + static sptr make(size_t itemsize, int a, int b); + + virtual void set_a(int b) = 0; + virtual void set_b(int b) = 0; + virtual int a() const = 0; + virtual int b() const = 0; + }; + + } /* namespace ctrlport */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_NOP_H */ + diff --git a/gnuradio-core/src/lib/runtime/nop_impl.cc b/gnuradio-core/src/lib/runtime/nop_impl.cc new file mode 100644 index 0000000000..1aaba1b0cd --- /dev/null +++ b/gnuradio-core/src/lib/runtime/nop_impl.cc @@ -0,0 +1,120 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "nop_impl.h" +#include <gr_io_signature.h> + +namespace gr { + namespace ctrlport { + + nop::sptr + nop::make(size_t itemsize, int a, int b) + { + return gnuradio::get_initial_sptr + (new nop_impl(itemsize, a, b)); + } + + + nop_impl::nop_impl(size_t itemsize, int a, int b) + : gr_sync_block("nop", + gr_make_io_signature(1, 1, itemsize), + gr_make_io_signature(0, 0, 0)) + { + set_a(a); + set_b(b); + setup_rpc(); + } + + nop_impl::~nop_impl() + { + } + + void + nop_impl::set_a(int a) + { + d_a = a; + } + + void + nop_impl::set_b(int b) + { + d_b = b; + } + + int + nop_impl::a() const + { + return d_a; + } + + int + nop_impl::b() const + { + return d_b; + } + + int + nop_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + return noutput_items; + } + + void + nop_impl::setup_rpc() + { + d_get_32i_rpcs.push_back(get_32i_sptr + (new get_32i_t(d_name, "a", this, unique_id(), + &nop_impl::a, + pmt::mp(-128), pmt::mp(127), pmt::mp(0), + "", "Value of a", + RPC_PRIVLVL_MIN, DISPTIMESERIESF))); + + d_get_32i_rpcs.push_back(get_32i_sptr + (new get_32i_t(d_name, "b", this, unique_id(), + &nop_impl::b, + pmt::mp(-128), pmt::mp(127), pmt::mp(0), + "", "Value of b", + RPC_PRIVLVL_MIN, DISPTIMESERIESF))); + + d_set_32i_rpcs.push_back(set_32i_sptr + (new set_32i_t(d_name, "a", this, unique_id(), + &nop_impl::set_a, + pmt::mp(-128), pmt::mp(127), pmt::mp(0), + "", "Value of a", + RPC_PRIVLVL_MIN, DISPNULL))); + + d_set_32i_rpcs.push_back(set_32i_sptr + (new set_32i_t(d_name, "b", this, unique_id(), + &nop_impl::set_b, + pmt::mp(-128), pmt::mp(127), pmt::mp(0), + "", "Value of b", + RPC_PRIVLVL_MIN, DISPNULL))); + } + + } /* namespace ctrlport */ +} /* namespace gr */ diff --git a/gnuradio-core/src/lib/runtime/nop_impl.h b/gnuradio-core/src/lib/runtime/nop_impl.h new file mode 100644 index 0000000000..a39ddac8f9 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/nop_impl.h @@ -0,0 +1,67 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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_CTRLPORT_NOP_IMPL_H +#define INCLUDED_CTRLPORT_NOP_IMPL_H + +#include <ctrlport/nop.h> +#include <ctrlport/rpcregisterhelpers.h> +#include <boost/thread/shared_mutex.hpp> + +namespace gr { + namespace ctrlport { + + class CTRLPORT_API nop_impl : public nop + { + private: + typedef rpcbasic_register_get<nop_impl, int> get_32i_t; + typedef rpcbasic_register_set<nop_impl, int> set_32i_t; + + typedef boost::shared_ptr<get_32i_t> get_32i_sptr; + typedef boost::shared_ptr<set_32i_t> set_32i_sptr; + + std::vector<get_32i_sptr> d_get_32i_rpcs; + std::vector<set_32i_sptr> d_set_32i_rpcs; + + void setup_rpc(); + + int d_a, d_b; + + public: + nop_impl(size_t itemsize, int a, int b); + ~nop_impl(); + + void set_a(int a); + void set_b(int b); + int a() const; + int b() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace ctrlport */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_NOP_IMPL_H */ + diff --git a/gnuradio-core/src/lib/runtime/pycallback_object.h b/gnuradio-core/src/lib/runtime/pycallback_object.h new file mode 100644 index 0000000000..c27bf00508 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/pycallback_object.h @@ -0,0 +1,183 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <iostream> +#include <rpcregisterhelpers.h> +#include <ice_application_base.h> +#include <IcePy_Communicator.h> +#include <pythread.h> + +enum pyport_t { + PYPORT_STRING, + PYPORT_FLOAT +}; + +class Instance +{ +public: + static boost::shared_ptr<ice_application_common> get_application() + { + return ice_application_common::Instance(); + } + static Ice::CommunicatorPtr get_swig_communicator() + { + return get_application()->communicator(); + } +}; + +int pycallback_object_count = 500; + +// a simple to-PMT converter template class-function +template <class myType> class pmt_assist +{ +public: + static pmt::pmt_t make(myType _val) + { + return pmt::mp(_val); + } +}; + +/* template specializations for vectors that cant use pmt::mp() */ +template<> +pmt::pmt_t pmt_assist<std::vector<float> >::make(std::vector<float> _val) +{ + return pmt::pmt_init_f32vector(_val.size(), &_val[0]); +} + +template<> +pmt::pmt_t pmt_assist<std::vector<gr_complex> >::make(std::vector<gr_complex> _val) +{ + return pmt::pmt_init_c32vector(_val.size(), &_val[0]); +} + +template <class myType> class pycallback_object +{ +public: + pycallback_object(std::string name, std::string functionbase, + std::string units, std::string desc, + myType min, myType max, myType deflt, + DisplayType dtype) : + d_callback(NULL), + d_rpc(name, functionbase.c_str(), this, pycallback_object_count++, + &pycallback_object::get, pmt_assist<myType>::make(min), + pmt_assist<myType>::make(max), pmt_assist<myType>::make(deflt), + units.c_str(), desc.c_str(), RPC_PRIVLVL_MIN, dtype) + //pmt::mp(min), pmt::mp(max), pmt::mp(deflt), units.c_str(), desc.c_str(), RPC_PRIVLVL_MIN, dtype) + { + d_callback = NULL; + } + + myType get() { + myType rVal; + if(d_callback == NULL) { + printf("WARNING: pycallback_object get() called without py callback set!\n"); + return rVal; + } + else { + // obtain PyGIL + PyGILState_STATE state = PyGILState_Ensure(); + + PyObject *func; + //PyObject *arglist; + PyObject *result; + + func = (PyObject *) d_callback; // Get Python function + //arglist = Py_BuildValue(""); // Build argument list + result = PyEval_CallObject(func,NULL); // Call Python + //result = PyEval_CallObject(func,arglist); // Call Python + //Py_DECREF(arglist); // Trash arglist + if(result) { // If no errors, return double + rVal = pyCast(result); + } + Py_XDECREF(result); + + // release PyGIL + PyGILState_Release(state); + return rVal; + } + } + + void set_callback(PyObject *cb) + { + d_callback = cb; + } + +private: + PyObject* d_callback; + rpcbasic_register_get<pycallback_object, myType> d_rpc; + + myType pyCast(PyObject* obj) { + printf("TYPE NOT IMPLEMENTED!\n"); + assert(0); + }; +}; + + +// template specialization conversion functions +// get data out of the PyObject and into the real world +template<> +std::string pycallback_object<std::string>::pyCast(PyObject* obj) +{ + return std::string(PyString_AsString(obj)); +} + +template<> +double pycallback_object<double>::pyCast(PyObject* obj) +{ + return PyFloat_AsDouble(obj); +} + +template<> +float pycallback_object<float>::pyCast(PyObject* obj) +{ + return (float)PyFloat_AsDouble(obj); +} + +template<> +int pycallback_object<int>::pyCast(PyObject* obj) +{ + return PyInt_AsLong(obj); +} + +template<> +std::vector<float> pycallback_object<std::vector<float> >::pyCast(PyObject* obj) +{ + int size = PyObject_Size(obj); + std::vector<float> rval(size); + for(int i=0; i<size; i++) { + rval[i] = (float)PyFloat_AsDouble(PyList_GetItem(obj, i)); + } + return rval; +} + +template<> +std::vector<gr_complex> pycallback_object<std::vector<gr_complex> >::pyCast(PyObject* obj) +{ + int size = PyObject_Size(obj); + std::vector<gr_complex> rval(size); + for(int i=0; i<size; i++){ rval[i] = \ + gr_complex((float)PyComplex_RealAsDouble(PyList_GetItem(obj, i)), + (float)PyComplex_ImagAsDouble(PyList_GetItem(obj, i))); + } + return rval; +} +// TODO: add more template specializations as needed! diff --git a/gnuradio-core/src/lib/runtime/rpccallbackregister_base.h b/gnuradio-core/src/lib/runtime/rpccallbackregister_base.h new file mode 100644 index 0000000000..c8f60b310c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpccallbackregister_base.h @@ -0,0 +1,96 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCCALLBACKREGISTER_BASE_H +#define RPCCALLBACKREGISTER_BASE_H + +#include <gruel/msg_accepter.h> +#include <gruel/msg_producer.h> + +enum DisplayType { + DISPNULL, + DISPTIMESERIESF, + DISPTIMESERIESC, + DISPXYSCATTER, + DISPXYLINE +}; + +enum priv_lvl_t { + RPC_PRIVLVL_ALL = 0, + RPC_PRIVLVL_MIN = 9, + RPC_PRIVLVL_NONE = 10 +}; + +enum KnobType { + KNOBBOOL, KNOBCHAR, KNOBINT, KNOBFLOAT, + KNOBDOUBLE, KNOBSTRING, KNOBLONG, KNOBVECBOOL, + KNOBVECCHAR, KNOBVECINT, KNOBVECFLOAT, KNOBVECDOUBLE, + KNOBVECSTRING, KNOBVECLONG +}; + +struct callbackregister_base +{ + struct callback_base_t + { + public: + callback_base_t(const priv_lvl_t priv_, const std::string& units_, + const DisplayType display_, const std::string& desc_, + const pmt::pmt_t min_, const pmt::pmt_t max_, const pmt::pmt_t def) + : priv(priv_), units(units_), description(desc_), + min(min_), max(max_), defaultvalue(def), display(display_) + { + } + + priv_lvl_t priv; + std::string units, description; + pmt::pmt_t min, max, defaultvalue; + DisplayType display; + }; + + template<typename T, typename Tsptr> + class callback_t : public callback_base_t + { + public: + callback_t(T* callback_, priv_lvl_t priv_, + const std::string& units_, const DisplayType display_, const:: std::string& desc_, + const pmt::pmt_t& min_, const pmt::pmt_t& max_, const pmt::pmt_t& def_) : + callback_base_t(priv_, units_, display_, desc_, min_, max_, def_), + callback(callback_) + { + } + + Tsptr callback; + }; + + typedef callback_t<gruel::msg_accepter, gruel::msg_accepter_sptr> configureCallback_t; + typedef callback_t<gruel::msg_producer, gruel::msg_producer_sptr> queryCallback_t; + + callbackregister_base() {;} + virtual ~callbackregister_base() {;} + + virtual void registerConfigureCallback(const std::string &id, const configureCallback_t callback) = 0; + virtual void unregisterConfigureCallback(const std::string &id) = 0; + virtual void registerQueryCallback(const std::string &id, const queryCallback_t callback) = 0; + virtual void unregisterQueryCallback(const std::string &id) = 0; +}; + +#endif /* RPCCALLBACKREGISTER_BASE_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcmanager.cc b/gnuradio-core/src/lib/runtime/rpcmanager.cc new file mode 100644 index 0000000000..4d164b63f3 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcmanager.cc @@ -0,0 +1,72 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcmanager.h> +#include <iostream> +#include <stdexcept> + +bool rpcmanager::booter_registered(false); +bool rpcmanager::aggregator_registered(false); +rpcserver_booter_base* rpcmanager::boot(0); +std::auto_ptr<rpcserver_booter_aggregator> rpcmanager::aggregator(0); + +rpcmanager::rpcmanager() {;} + +rpcmanager::~rpcmanager() +{ + if(boot) + delete boot; +} + +rpcserver_booter_base* +rpcmanager::get() +{ + if(aggregator_registered) { + return aggregator.get(); + } + else if(booter_registered) { + return boot; + } + assert(booter_registered || aggregator_registered); + return boot; +} + +void +rpcmanager::register_booter(rpcserver_booter_base* booter) +{ + if(make_aggregator && !aggregator_registered) { + aggregator.reset(new rpcserver_booter_aggregator()); + aggregator_registered = true; + } + + if(aggregator_registered) { + rpcmanager::rpcserver_booter_base_sptr bootreg(booter); + aggregator->agg()->registerServer(bootreg); + } + else if(!booter_registered) { + boot = booter; + booter_registered = true; + } + else { + throw std::runtime_error("rpcmanager: Aggregator not in use, and a rpc booter is already registered\n"); + } +} diff --git a/gnuradio-core/src/lib/runtime/rpcmanager.h b/gnuradio-core/src/lib/runtime/rpcmanager.h new file mode 100644 index 0000000000..8cb176b2e5 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcmanager.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCMANAGER_H +#define RPCMANAGER_H + +#include <gr_core_api.h> +#include <rpcmanager_base.h> +#include <rpcserver_booter_aggregator.h> +#include <memory> +#include <iostream> + +class GR_CORE_API rpcmanager : public virtual rpcmanager_base +{ + public: + rpcmanager(); + ~rpcmanager(); + + static rpcserver_booter_base* get(); + + static void register_booter(rpcserver_booter_base* booter); + + template<typename T> class rpcserver_booter_register_helper + { + public: + rpcserver_booter_register_helper() { + rpcmanager::register_booter(new T()); + } + + //TODO: unregister + }; + + private: + static bool make_aggregator, booter_registered, aggregator_registered; + static void rpcserver_booter_base_sptr_dest( rpcserver_booter_base* b) {;} + static rpcserver_booter_base* boot; + static std::auto_ptr<rpcserver_booter_aggregator> aggregator; +}; + +#endif /* RPCMANAGER_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcmanager_base.h b/gnuradio-core/src/lib/runtime/rpcmanager_base.h new file mode 100644 index 0000000000..60425c4a15 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcmanager_base.h @@ -0,0 +1,46 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCMANAGER_BASE_H +#define RPCMANAGER_BASE_H + +#include <boost/shared_ptr.hpp> + +class rpcserver_booter_base; +//class rpcserver_booter_aggregator; + +class rpcmanager_base +{ + public: + typedef boost::shared_ptr<rpcserver_booter_base> rpcserver_booter_base_sptr; + + rpcmanager_base() {;} + ~rpcmanager_base() {;} + + //static rpcserver_booter_base* get(); + + //static void register_booter(rpcserver_booter_base_sptr booter); + +private: +}; + +#endif /* RPCMANAGER_BASE_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcpmtconverters_ice.cc b/gnuradio-core/src/lib/runtime/rpcpmtconverters_ice.cc new file mode 100644 index 0000000000..31953fd09c --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcpmtconverters_ice.cc @@ -0,0 +1,113 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcpmtconverters_ice.h> +#include <Ice/Ice.h> +#include <gnuradio.h> + +GNURadio::KnobPtr +rpcpmtconverter::from_pmt(const pmt::pmt_t& knob, const Ice::Current& c) +{ + if(pmt::pmt_is_real(knob)) { + return new GNURadio::KnobD(Ice::Double(pmt::pmt_to_double(knob))); + } + else if(pmt::pmt_is_symbol(knob)) { + std::string stuff = pmt::pmt_symbol_to_string(knob); + if(stuff.length() != 1) { + return new GNURadio::KnobS(stuff); + } + else { + return new GNURadio::KnobC(stuff[0]); + } + + //TODO: FLOAT!!! + } + else if(pmt::pmt_is_integer(knob)) { + return new GNURadio::KnobI(pmt::pmt_to_long(knob)); + } + else if(pmt::pmt_is_bool(knob)) { + return new GNURadio::KnobB(pmt::pmt_to_bool(knob)); + } + else if(pmt::pmt_is_uint64(knob)) { + return new GNURadio::KnobL(pmt::pmt_to_uint64(knob)); + //const std::complex<float> *pmt_c32vector_elements(pmt_t v, size_t &len); //< len is in elements + } + else if(pmt::pmt_is_c32vector(knob)) { // c32 sent as interleaved floats + size_t size(pmt::pmt_length(knob)); + const float* start((const float*) pmt::pmt_c32vector_elements(knob,size)); + return new GNURadio::KnobVecF(std::vector<float>(start,start+size*2)); + } + else if(pmt::pmt_is_f32vector(knob)) { + size_t size(pmt::pmt_length(knob)); + const float* start((const float*) pmt::pmt_f32vector_elements(knob,size)); + return new GNURadio::KnobVecF(std::vector<float>(start,start+size)); + } + else { + std::cerr << "Error: Don't know how to handle Knob Type (from): " << std::endl; assert(0);} + //TODO: VECTORS!!! + return new GNURadio::Knob(); +} + +pmt::pmt_t +rpcpmtconverter::to_pmt(const GNURadio::KnobPtr& knob, const Ice::Current& c) +{ + std::string id(knob->ice_id(c).substr(12)); + if(id == "KnobD") { + GNURadio::KnobDPtr k(GNURadio::KnobDPtr::dynamicCast(knob)); + return pmt::mp(k->value); + } + else if(id == "KnobF") { + GNURadio::KnobFPtr k(GNURadio::KnobFPtr::dynamicCast(knob)); + return pmt::mp(k->value); + } + else if(id == "KnobI") { + GNURadio::KnobIPtr k(GNURadio::KnobIPtr::dynamicCast(knob)); + return pmt::mp(k->value); + } + else if(id == "KnobS") { + GNURadio::KnobSPtr k(GNURadio::KnobSPtr::dynamicCast(knob)); + return pmt::pmt_string_to_symbol(k->value); + } + else if(id == "KnobB") { + GNURadio::KnobBPtr k(GNURadio::KnobBPtr::dynamicCast(knob)); + return pmt::mp(k->value); + } + else if(id == "KnobC") { + GNURadio::KnobCPtr k(GNURadio::KnobCPtr::dynamicCast(knob)); + return pmt::mp(k->value); + } + else if(id == "KnobL") { + GNURadio::KnobLPtr k(GNURadio::KnobLPtr::dynamicCast(knob)); + return pmt::mp((long)k->value); + } + //else if(id == "KnobVecF") { + // GNURadio::KnobVecFPtr k(GNURadio::KnobVecFPtr::dynamicCast(knob)); + // return pmt::mp(k->value); + //TODO: FLOAT!!! + //TODO: VECTORS!!! + + else { + std::cerr << "Error: Don't know how to handle Knob Type: " << id << std::endl; assert(0); + } + + return pmt::pmt_t(); +} diff --git a/gnuradio-core/src/lib/runtime/rpcpmtconverters_ice.h b/gnuradio-core/src/lib/runtime/rpcpmtconverters_ice.h new file mode 100644 index 0000000000..4403b96a2a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcpmtconverters_ice.h @@ -0,0 +1,35 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCPMTCONVERTERS_ICE_H +#define RPCPMTCONVERTERS_ICE_H + +#include <gruel/pmt.h> +#include <gnuradio.h> + +namespace rpcpmtconverter +{ + pmt::pmt_t to_pmt(const GNURadio::KnobPtr& knob, const Ice::Current& c); + GNURadio::KnobPtr from_pmt(const pmt::pmt_t& knob, const Ice::Current& c); +} + +#endif /* RPCPMTCONVERTERS_ICE_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcregisterhelpers.h b/gnuradio-core/src/lib/runtime/rpcregisterhelpers.h new file mode 100644 index 0000000000..bbe4f6c7d3 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcregisterhelpers.h @@ -0,0 +1,489 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCREGISTERHELPERS_H +#define RPCREGISTERHELPERS_H + +#include <stdio.h> +#include <sstream> +#include <iostream> +#include <rpcserver_booter_base.h> +#include <rpcmanager.h> +#include <rpcserver_selector.h> +#include <rpcserver_base.h> + +// Base classes +template<typename T, typename Tto> class rpcextractor_base + : public virtual gruel::msg_accepter +{ +public: + rpcextractor_base(T* source, void (T::*func)(Tto)) : + _source(source), _func(func) {;} + ~rpcextractor_base() {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) { + throw std::runtime_error("rpcextractor_base: no post defined for this data type.\n"); + } + +protected: + T* _source; + void (T::*_func)(Tto); +}; + +template<typename T, typename Tto> +class rpcbasic_extractor : public virtual rpcextractor_base<T,Tto> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(Tto)) : + rpcextractor_base<T,Tto>(source, func) + {;} +}; + +template<typename T, typename Tfrom> +class rpcinserter_base : public virtual gruel::msg_producer +{ +public: + rpcinserter_base(T* source, Tfrom (T::*func)()) : _source(source), _func(func) {;} + rpcinserter_base() {;} + + pmt::pmt_t retrieve() { assert(0); return pmt::pmt_t(); } + +protected: + T* _source; + Tfrom (T::*_func)(); +}; + +template<typename T, typename Tfrom> +class rpcbasic_inserter : + public virtual rpcinserter_base<T,Tfrom> +{ +public: + rpcbasic_inserter(T* source, Tfrom (T::*func)()const) + : rpcinserter_base<T,Tfrom>(source, func) + {;} + + rpcbasic_inserter(T* source, Tfrom (T::*func)()) + : rpcinserter_base<T,Tfrom>(source, func) + {;} + + pmt::pmt_t retrieve() + { + return pmt::mp((rpcinserter_base<T,Tfrom>:: + _source->*rpcinserter_base<T,Tfrom>::_func)()); + } +}; + +// Specialized Extractor Templates +template<typename T> +class rpcbasic_extractor<T,double> : public virtual rpcextractor_base<T,double> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(double)) + : rpcextractor_base<T,double>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,double>::_source->*rpcextractor_base<T,double>::_func) + (pmt::pmt_to_double(msg)); + } +}; + +template<typename T> +class rpcbasic_extractor<T,float> : public virtual rpcextractor_base<T,float> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(float)) + : rpcextractor_base<T,float>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,float>::_source->*rpcextractor_base<T,float>::_func) + (pmt::pmt_to_double(msg)); + } +}; + +template<typename T> +class rpcbasic_extractor<T,long> : public virtual rpcextractor_base<T,long> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(long)) + : rpcextractor_base<T,long>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,long>::_source->*rpcextractor_base<T,long>::_func) + (pmt::pmt_to_long(msg)); + } +}; + +template<typename T> +class rpcbasic_extractor<T,int> : public virtual rpcextractor_base<T,int> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(int)) + : rpcextractor_base<T,int>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,int>::_source->*rpcextractor_base<T,int>::_func) + (pmt::pmt_to_long(msg)); + } +}; + +template<typename T> +class rpcbasic_extractor<T,bool> : public virtual rpcextractor_base<T,bool> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(bool)) + : rpcextractor_base<T,bool>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,bool>::_source->*rpcextractor_base<T,bool>::_func) + (pmt::pmt_to_bool(msg)); + } +}; + +template<typename T> +class rpcbasic_extractor<T,std::complex<double> > + : public virtual rpcextractor_base<T,std::complex<double> > +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(std::complex<double>)) + : rpcextractor_base<T,std::complex<double> >(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,std::complex<double> >:: + _source->*rpcextractor_base<T,std::complex<double> >::_func)(pmt::pmt_to_complex(msg)); + } +}; + +template<typename T> +class rpcbasic_extractor<T,std::string> + : public virtual rpcextractor_base<T,std::string> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(std::string)) + : rpcextractor_base<T,std::string>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,std::string>:: + _source->*rpcextractor_base<T,std::string>::_func)(pmt::pmt_symbol_to_string(msg)); + } +}; + +template<typename T> +class rpcbasic_inserter<T,uint64_t> : public virtual rpcinserter_base<T,uint64_t> +{ +public: + rpcbasic_inserter(T* source, uint64_t (T::*func)() const) + : rpcinserter_base<T,uint64_t>(source, func) + {;} + + rpcbasic_inserter(T* source, uint64_t (T::*func)()) + : rpcinserter_base<T,uint64_t>(source, func) + {;} + + pmt::pmt_t retrieve() + { + return pmt::pmt_from_uint64((rpcinserter_base<T,uint64_t>:: + _source->*rpcinserter_base<T,uint64_t>::_func)()); + } +}; + +template<typename T> +class rpcbasic_inserter<T,std::vector< std::complex<float> > > + : public virtual rpcinserter_base<T,std::vector< std::complex<float> > > +{ +public: + rpcbasic_inserter(T* source, std::vector<std::complex<float> > (T::*func)() const) + : rpcinserter_base<T,std::vector<std::complex<float> > >(source, func) + {;} + + rpcbasic_inserter(T* source, std::vector<std::complex<float> > (T::*func)()) + : rpcinserter_base<T,std::vector<std::complex<float> > >(source, func) + {;} + + pmt::pmt_t retrieve() + { + std::vector< std::complex<float> > + vec((rpcinserter_base<T,std::vector<std::complex<float> > >:: + _source->*rpcinserter_base<T,std::vector< std::complex<float> > >::_func)()); + return pmt::pmt_init_c32vector(vec.size(), &vec[0]); + } +}; + +template<typename T> +class rpcbasic_inserter<T,std::vector< float> > + : public virtual rpcinserter_base<T,std::vector< float > > +{ +public: + rpcbasic_inserter(T* source, std::vector<float> (T::*func)() const) + : rpcinserter_base<T,std::vector<float > >(source, func) + {;} + + rpcbasic_inserter(T* source, std::vector<float> (T::*func)()) + : rpcinserter_base<T,std::vector<float> >(source, func) + {;} + + pmt::pmt_t retrieve() + { + std::vector< float > vec((rpcinserter_base<T,std::vector<float> >:: + _source->*rpcinserter_base<T,std::vector< float> >::_func)()); + return pmt::pmt_init_f32vector(vec.size(), &vec[0]); + } +}; + +template <typename T> +struct rpc_register_base +{ + rpc_register_base() {count++;} +protected: static int count; +}; + +// Base class to inherit from and create universal shared pointers. +class rpcbasic_base +{ +public: + rpcbasic_base() {} + virtual ~rpcbasic_base() {}; +}; + +typedef boost::shared_ptr<rpcbasic_base> rpcbasic_sptr; + +template<typename T, typename Tto> +struct rpcbasic_register_set : public rpcbasic_base +{ + rpcbasic_register_set(const std::string& namebase, + const char* functionbase, T* obj, + const unsigned int serial, + void (T::*function)(Tto), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) + { + d_min = min; + d_max = max; + d_def = def; + d_units = units_; + d_desc = desc_; + d_minpriv = minpriv_; + d_display = display_; +#ifdef RPCSERVER_ENABLED + callbackregister_base::configureCallback_t + extractor(new rpcbasic_extractor<T,Tto>(obj, function), + minpriv_, std::string(units_), + display_, std::string(desc_), min, max, def); + std::ostringstream oss(std::ostringstream::out); + oss << namebase << serial << "::" << functionbase; d_id = oss.str(); + //std::cerr << "REGISTERING SET: " << d_id << " " << desc_ << std::endl; + rpcmanager::get()->i()->registerConfigureCallback(d_id, extractor); +#endif + } + + ~rpcbasic_register_set() + { +#ifdef RPCSERVER_ENABLED + rpcmanager::get()->i()->unregisterConfigureCallback(d_id); +#endif + } + + + pmt::pmt_t min() const { return d_min; } + pmt::pmt_t max() const { return d_max; } + pmt::pmt_t def() const { return d_def; } + std::string units() const { return d_units; } + std::string description() const { return d_desc; } + priv_lvl_t privilege_level() const { return d_minpriv; } + DisplayType default_display() const { return d_display; } + + void set_min(pmt::pmt_t p) { d_min = p; } + void set_max(pmt::pmt_t p) { d_max = p; } + void set_def(pmt::pmt_t p) { d_def = p; } + void units(std::string u) { d_units = u; } + void description(std::string d) { d_desc = d; } + void privilege_level(priv_lvl_t p) { d_minpriv = p; } + void default_display(DisplayType d) { d_display = d; } + +private: + std::string d_id; + pmt::pmt_t d_min, d_max, d_def; + std::string d_units, d_desc; + priv_lvl_t d_minpriv; + DisplayType d_display; +}; + + +template<typename T, typename Tfrom> +class rpcbasic_register_get : public rpcbasic_base +{ +public: + // primary constructor to allow for T get() functions + rpcbasic_register_get(const std::string& namebase, + const char* functionbase, T* obj, + const int serial, + Tfrom (T::*function)(), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) + { + d_min = min; + d_max = max; + d_def = def; + d_units = units_; + d_desc = desc_; + d_minpriv = minpriv_; + d_display = display_; +#ifdef RPCSERVER_ENABLED + callbackregister_base::queryCallback_t + inserter(new rpcbasic_inserter<T,Tfrom>(obj, function), + minpriv_, std::string(units_), display_, std::string(desc_), min, max, def); + std::ostringstream oss(std::ostringstream::out); + oss << namebase << serial << "::" << functionbase; + d_id = oss.str(); + //std::cerr << "REGISTERING GET: " << d_id << " " << desc_ << std::endl; + rpcmanager::get()->i()->registerQueryCallback(d_id, inserter); +#endif + } + + // alternate constructor to allow for T get() const functions + rpcbasic_register_get(const std::string& namebase, + const char* functionbase, T* obj, + const int serial, + Tfrom (T::*function)() const, + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) + { + d_min = min; + d_max = max; + d_def = def; + d_units = units_; + d_desc = desc_; + d_minpriv = minpriv_; + d_display = display_; +#ifdef RPCSERVER_ENABLED + callbackregister_base::queryCallback_t + inserter(new rpcbasic_inserter<T,Tfrom>(obj, (Tfrom (T::*)())function), + minpriv_, std::string(units_), display_, std::string(desc_), min, max, def); + std::ostringstream oss(std::ostringstream::out); + oss << namebase << serial << "::" << functionbase; + d_id = oss.str(); + //std::cerr << "REGISTERING GET CONST: " << d_id << " " << desc_ << " " << display_ << std::endl; + rpcmanager::get()->i()->registerQueryCallback(d_id, inserter); +#endif + } + + ~rpcbasic_register_get() + { +#ifdef RPCSERVER_ENABLED + rpcmanager::get()->i()->unregisterQueryCallback(d_id); +#endif + } + + pmt::pmt_t min() const { return d_min; } + pmt::pmt_t max() const { return d_max; } + pmt::pmt_t def() const { return d_def; } + std::string units() const { return d_units; } + std::string description() const { return d_desc; } + priv_lvl_t privilege_level() const { return d_minpriv; } + DisplayType default_display() const { return d_display; } + + void set_min(pmt::pmt_t p) { d_min = p; } + void set_max(pmt::pmt_t p) { d_max = p; } + void set_def(pmt::pmt_t p) { d_def = p; } + void units(std::string u) { d_units = u; } + void description(std::string d) { d_desc = d; } + void privilege_level(priv_lvl_t p) { d_minpriv = p; } + void default_display(DisplayType d) { d_display = d; } + +private: + std::string d_id; + pmt::pmt_t d_min, d_max, d_def; + std::string d_units, d_desc; + priv_lvl_t d_minpriv; + DisplayType d_display; +}; + +/* + * This class can wrap a pre-existing variable type for you + * it will define the getter and rpcregister call for you. + * + * It should be used for read-only getters. + * + */ +template<typename Tfrom> +class rpcbasic_register_variable : public rpcbasic_base +{ +private: + rpcbasic_register_get< rpcbasic_register_variable<Tfrom>, Tfrom > d_rpc_reg; + Tfrom *d_variable; + Tfrom get() { return *d_variable; } + +public: + // empty constructor which should never be called but needs to exist for ues in varous STL data structures + rpcbasic_register_variable() : + d_rpc_reg("FAIL", "FAIL", this, -1, &rpcbasic_register_variable::get, + pmt::PMT_NIL, pmt::PMT_NIL, pmt::PMT_NIL, DISPNULL, + "FAIL", "FAIL", RPC_PRIVLVL_MIN), + d_variable(NULL) + { + std::cerr << "ERROR: rpcbasic_register_variable called with no args. " + << "If this happens, someone has tried to use rpcbasic_register_variable incorrectly.\n"; + assert(0); + }; + + void set(Tfrom* _variable) { d_variable = _variable; } + + rpcbasic_register_variable(const std::string& namebase, + const char* functionbase, + const int serial, + Tfrom *variable, + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_=DISPNULL) : + d_rpc_reg(namebase,functionbase,this,serial,&rpcbasic_register_variable::get, + min,max,def,units_,desc_,minpriv_,display_), + d_variable(variable) + { + //std::cerr << "REGISTERING VAR: " << serial << " " << desc_ << std::endl; + } +}; + +#endif diff --git a/gnuradio-core/src/lib/runtime/rpcserver_aggregator.cc b/gnuradio-core/src/lib/runtime/rpcserver_aggregator.cc new file mode 100644 index 0000000000..d750d64905 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_aggregator.cc @@ -0,0 +1,93 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcserver_aggregator.h> +#include <rpcserver_booter_base.h> +#include <iostream> +#include <sstream> +#include <stdexcept> + +rpcserver_aggregator::rpcserver_aggregator() + : d_type(std::string("aggregator")) +{;} + +rpcserver_aggregator::~rpcserver_aggregator() +{;} + +const std::string& +rpcserver_aggregator::type() +{ + return d_type; +} + +const std::vector<std::string>& +rpcserver_aggregator::registeredServers() +{ + return d_registeredServers; +} + +void +rpcserver_aggregator::registerConfigureCallback(const std::string &id, + const configureCallback_t callback) +{ + std::for_each(d_serverlist.begin(), d_serverlist.end(), + registerConfigureCallback_f<rpcmanager_base::rpcserver_booter_base_sptr, configureCallback_t>(id, callback)); +} + +void +rpcserver_aggregator::unregisterConfigureCallback(const std::string &id) +{ + std::for_each(d_serverlist.begin(), d_serverlist.end(), + unregisterConfigureCallback_f<rpcmanager_base::rpcserver_booter_base_sptr, configureCallback_t>(id)); +} + +void +rpcserver_aggregator::registerQueryCallback(const std::string &id, const queryCallback_t callback) +{ + std::for_each(d_serverlist.begin(), d_serverlist.end(), + registerQueryCallback_f<rpcmanager_base::rpcserver_booter_base_sptr, queryCallback_t>(id, callback)); +} + +void +rpcserver_aggregator::unregisterQueryCallback(const std::string &id) +{ + std::for_each(d_serverlist.begin(), d_serverlist.end(), + unregisterQueryCallback_f<rpcmanager_base::rpcserver_booter_base_sptr, queryCallback_t>(id)); +} + +void +rpcserver_aggregator::registerServer(rpcmanager_base::rpcserver_booter_base_sptr server) +{ + std::vector<std::string>::iterator it(std::find(d_registeredServers.begin(), + d_registeredServers.end(), + server->type())); + if(it != d_registeredServers.end()) { + d_serverlist.push_back(server); + d_registeredServers.push_back(server->type()); + } + else { + std::stringstream s; + s << "rpcserver_aggregator::registerServer: server of type " + << server->type() << " already registered" << std::endl; + throw std::runtime_error(s.str().c_str()); + } +} diff --git a/gnuradio-core/src/lib/runtime/rpcserver_aggregator.h b/gnuradio-core/src/lib/runtime/rpcserver_aggregator.h new file mode 100644 index 0000000000..050d9bb1e5 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_aggregator.h @@ -0,0 +1,100 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCSERVER_AGGREGATOR_H +#define RPCSERVER_AGGREGATOR_H + +#include <vector> +#include <string> +#include <rpcserver_base.h> +#include <rpcmanager_base.h> + +class rpcserver_aggregator : public virtual rpcserver_base +{ +public: + rpcserver_aggregator(); + virtual ~rpcserver_aggregator(); + + void registerConfigureCallback(const std::string &id, const configureCallback_t callback); + void unregisterConfigureCallback(const std::string &id); + + void registerQueryCallback(const std::string &id, const queryCallback_t callback); + void unregisterQueryCallback(const std::string &id); + + void registerServer(rpcmanager_base::rpcserver_booter_base_sptr server); + + const std::string& type(); + + const std::vector<std::string>& registeredServers(); + +private: + template<class T, typename Tcallback> + struct registerConfigureCallback_f: public std::unary_function<T,void> + { + registerConfigureCallback_f(const std::string &_id, const Tcallback _callback) + : id(_id), callback(_callback) + {;} + + void operator()(T& x) { x->i()->registerConfigureCallback(id, callback); } + const std::string& id; const Tcallback& callback; + }; + + template<class T, typename Tcallback> + struct unregisterConfigureCallback_f: public std::unary_function<T,void> + { + unregisterConfigureCallback_f(const std::string &_id) + : id(_id) + {;} + + void operator()(T& x) { x->i()->unregisterConfigureCallback(id); } + const std::string& id; + }; + + template<class T, typename Tcallback> + struct registerQueryCallback_f: public std::unary_function<T,void> + { + registerQueryCallback_f(const std::string &_id, const Tcallback _callback) + : id(_id), callback(_callback) + {;} + + void operator()(T& x) { x->i()->registerQueryCallback(id, callback); } + const std::string& id; const Tcallback& callback; + }; + + template<class T, typename Tcallback> + struct unregisterQueryCallback_f: public std::unary_function<T,void> + { + unregisterQueryCallback_f(const std::string &_id) + : id(_id) + {;} + + void operator()(T& x) { x->i()->unregisterQueryCallback(id); } + const std::string& id; + }; + + const std::string d_type; + typedef std::vector<rpcmanager_base::rpcserver_booter_base_sptr> rpcServerMap_t; + std::vector<std::string> d_registeredServers; + rpcServerMap_t d_serverlist; +}; + +#endif /* RPCSERVER_AGGREGATOR_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcserver_base.h b/gnuradio-core/src/lib/runtime/rpcserver_base.h new file mode 100644 index 0000000000..bc985c8d53 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_base.h @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCSERVER_BASE_H +#define RPCSERVER_BASE_H + +#include <rpccallbackregister_base.h> + +class rpcserver_base : public virtual callbackregister_base +{ +public: + rpcserver_base() : cur_priv(RPC_PRIVLVL_ALL) {;} + virtual ~rpcserver_base() {;} + + virtual void registerConfigureCallback(const std::string &id, const configureCallback_t callback) = 0; + virtual void unregisterConfigureCallback(const std::string &id) = 0; + virtual void registerQueryCallback(const std::string &id, const queryCallback_t callback) = 0; + virtual void unregisterQueryCallback(const std::string &id) = 0; + virtual void setCurPrivLevel(const priv_lvl_t priv) { cur_priv = priv; } + + typedef boost::shared_ptr<rpcserver_base> rpcserver_base_sptr; +protected: + priv_lvl_t cur_priv; + +private: +}; + +#endif /* RPCSERVER_BASE_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcserver_booter_aggregator.cc b/gnuradio-core/src/lib/runtime/rpcserver_booter_aggregator.cc new file mode 100644 index 0000000000..c4c1b03c15 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_booter_aggregator.cc @@ -0,0 +1,62 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcserver_booter_aggregator.h> + +rpcserver_booter_aggregator::rpcserver_booter_aggregator() : + d_type(std::string("aggregator")), server(new rpcserver_aggregator()) +{;} + +rpcserver_booter_aggregator::~rpcserver_booter_aggregator() +{;} + +rpcserver_base* +rpcserver_booter_aggregator::i() +{ + return &(*server); +} + +const std::string& +rpcserver_booter_aggregator::type() +{ + return d_type; +} + +const std::vector<std::string> +rpcserver_booter_aggregator::endpoints() +{ + std::vector<std::string> ep; + ep.push_back(std::string("TODO")); + return ep; +} + +const std::vector<std::string>& +rpcserver_booter_aggregator::registeredServers() +{ + return server->registeredServers(); +} + +rpcserver_aggregator* +rpcserver_booter_aggregator::agg() +{ + return &(*server); +} diff --git a/gnuradio-core/src/lib/runtime/rpcserver_booter_aggregator.h b/gnuradio-core/src/lib/runtime/rpcserver_booter_aggregator.h new file mode 100644 index 0000000000..da190a0be1 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_booter_aggregator.h @@ -0,0 +1,56 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCSERVER_BOOTER_AGGREGATOR +#define RPCSERVER_BOOTER_AGGREGATOR + +#include <gr_core_api.h> +#include <rpcserver_booter_base.h> +#include <rpcserver_aggregator.h> +#include <boost/shared_ptr.hpp> +#include <string> + +class rpcserver_server; + +class GR_CORE_API rpcserver_booter_aggregator : + public virtual rpcserver_booter_base +{ + public: + rpcserver_booter_aggregator(); + ~rpcserver_booter_aggregator(); + + rpcserver_base* i(); + const std::string& type(); + const std::vector<std::string> endpoints(); + + const std::vector<std::string>& registeredServers(); + + protected: + friend class rpcmanager; + rpcserver_aggregator* agg(); + +private: + std::string d_type; + boost::shared_ptr<rpcserver_aggregator> server; +}; + +#endif /* RPCSERVER_BOOTER_AGGREGATOR */ diff --git a/gnuradio-core/src/lib/runtime/rpcserver_booter_base.h b/gnuradio-core/src/lib/runtime/rpcserver_booter_base.h new file mode 100644 index 0000000000..682944dada --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_booter_base.h @@ -0,0 +1,44 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCSERVER_BOOTER_BASE +#define RPCSERVER_BOOTER_BASE + +#include <string> +#include <vector> + +class rpcserver_base; + +class rpcserver_booter_base +{ +public: + rpcserver_booter_base() {;} + virtual ~rpcserver_booter_base() {;} + + virtual rpcserver_base* i()=0; + virtual const std::vector<std::string> endpoints()=0; + virtual const std::string& type()=0; + +private: +}; + +#endif /* RPCSERVER_BOOTER_BASE */ diff --git a/gnuradio-core/src/lib/runtime/rpcserver_booter_ice.cc b/gnuradio-core/src/lib/runtime/rpcserver_booter_ice.cc new file mode 100644 index 0000000000..7cc8cc8938 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_booter_ice.cc @@ -0,0 +1,54 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcserver_ice.h> +#include <rpcserver_booter_ice.h> + +namespace { + static const char* const CONTROL_PORT_CLASS("ice"); + static const char* const CONTROL_PORT_NAME("ControlPort"); + static const char* const ENDPOINT_NAME("gnuradio"); +}; + +rpcserver_booter_ice::rpcserver_booter_ice() : + ice_server_template<rpcserver_base, rpcserver_ice, + rpcserver_booter_ice, GNURadio::ControlPortPtr> + (this, std::string(CONTROL_PORT_NAME), std::string(ENDPOINT_NAME)), + d_type(std::string(CONTROL_PORT_CLASS)) +{;} + +rpcserver_booter_ice::~rpcserver_booter_ice() +{;} + +rpcserver_base* +rpcserver_booter_ice::i() +{ + return ice_server_template<rpcserver_base, rpcserver_ice, + rpcserver_booter_ice, GNURadio::ControlPortPtr>::i(); +} + +const std::vector<std::string> +rpcserver_booter_ice::endpoints() +{ + return ice_server_template<rpcserver_base, rpcserver_ice, + rpcserver_booter_ice, GNURadio::ControlPortPtr>::endpoints(); +} diff --git a/gnuradio-core/src/lib/runtime/rpcserver_booter_ice.h b/gnuradio-core/src/lib/runtime/rpcserver_booter_ice.h new file mode 100644 index 0000000000..69dfcc7602 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_booter_ice.h @@ -0,0 +1,49 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCSERVER_BOOTER_ICE_H +#define RPCSERVER_BOOTER_ICE_H + +#include <rpcserver_booter_base.h> +#include <ice_server_template.h> +#include <gnuradio.h> + +class rpcserver_base; +class rpcserver_ice; + +class rpcserver_booter_ice : public virtual rpcserver_booter_base, + public virtual ice_server_template<rpcserver_base, rpcserver_ice, + rpcserver_booter_ice, GNURadio::ControlPortPtr> +{ +public: + rpcserver_booter_ice(); + ~rpcserver_booter_ice(); + + rpcserver_base* i(); + const std::string & type() {return d_type;} + const std::vector<std::string> endpoints(); + +private: + std::string d_type; +}; + +#endif /* RPCSERVER_BOOTER_ICE_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcserver_ice.cc b/gnuradio-core/src/lib/runtime/rpcserver_ice.cc new file mode 100644 index 0000000000..12229a0688 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_ice.cc @@ -0,0 +1,165 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcserver_ice.h> +#include <IceUtil/IceUtil.h> +#include <Ice/Ice.h> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <gruel/pmt.h> + +#define DEBUG 0 + +using namespace rpcpmtconverter; + +rpcserver_ice::rpcserver_ice() +{} + +rpcserver_ice::~rpcserver_ice() +{} + +void +rpcserver_ice::registerConfigureCallback(const std::string &id, + const configureCallback_t callback) +{ + { + ConfigureCallbackMap_t::const_iterator iter(d_setcallbackmap.find(id)); + if(iter != d_setcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_ice:: rpcserver_ice ERROR registering set, already registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + } + + if(DEBUG) + std::cout << "rpcserver_ice registering set: " << id << std::endl; + + d_setcallbackmap.insert(ConfigureCallbackMap_t::value_type(id, callback)); +} + +void +rpcserver_ice::unregisterConfigureCallback(const std::string &id) +{ + ConfigureCallbackMap_t::iterator iter(d_setcallbackmap.find(id)); + if(iter == d_setcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_ice:: rpcserver_ice ERROR unregistering set, not registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + + if(DEBUG) + std::cout << "rpcserver_ice unregistering set: " << id << std::endl; + + d_setcallbackmap.erase(iter); +} + +void +rpcserver_ice::registerQueryCallback(const std::string &id, + const queryCallback_t callback) +{ + { + QueryCallbackMap_t::const_iterator iter(d_getcallbackmap.find(id)); + if(iter != d_getcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_ice:: rpcserver_ice ERROR registering get, already registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + } + + if(DEBUG) + std::cout << "rpcserver_ice registering get: " << id << std::endl; + + d_getcallbackmap.insert(QueryCallbackMap_t::value_type(id, callback)); +} + +void +rpcserver_ice::unregisterQueryCallback(const std::string &id) +{ + QueryCallbackMap_t::iterator iter(d_getcallbackmap.find(id)); + if(iter == d_getcallbackmap.end()) { + std::stringstream s; + s << "rpcserver_ice:: rpcserver_ice ERROR unregistering get, registered: " + << id << std::endl; + throw std::runtime_error(s.str().c_str()); + } + + if(DEBUG) + std::cout << "rpcserver_ice unregistering get: " << id << std::endl; + + d_getcallbackmap.erase(iter); +} + +void +rpcserver_ice::set(const GNURadio::KnobMap& knobs, const Ice::Current& c) +{ + std::for_each(knobs.begin(), knobs.end(), + set_f<GNURadio::KnobMap::value_type,ConfigureCallbackMap_t> + (c, d_setcallbackmap, cur_priv)); +} + +GNURadio::KnobMap +rpcserver_ice::get(const GNURadio::KnobIDList& knobs, const Ice::Current& c) +{ + GNURadio::KnobMap outknobs; + + if(knobs.size() == 0) { + std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(), + get_all_f<QueryCallbackMap_t::value_type, QueryCallbackMap_t, GNURadio::KnobMap> + (c, d_getcallbackmap, cur_priv, outknobs)); + } + else { + std::for_each(knobs.begin(), knobs.end(), + get_f<GNURadio::KnobIDList::value_type, QueryCallbackMap_t> + (c, d_getcallbackmap, cur_priv, outknobs)); + } + return outknobs; +} + +GNURadio::KnobPropMap +rpcserver_ice::properties(const GNURadio::KnobIDList& knobs, const Ice::Current& c) +{ + GNURadio::KnobPropMap outknobs; + + if(knobs.size() == 0) { + std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(), + properties_all_f<QueryCallbackMap_t::value_type, + QueryCallbackMap_t,GNURadio::KnobPropMap>(c, d_getcallbackmap, cur_priv, outknobs)); + } + else { + std::for_each(knobs.begin(), knobs.end(), + properties_f<GNURadio::KnobIDList::value_type, + QueryCallbackMap_t, GNURadio::KnobPropMap>(c, d_getcallbackmap, cur_priv, outknobs)); + } + return outknobs; +} + +void +rpcserver_ice::shutdown(const Ice::Current& c) +{ + if(DEBUG) + std::cout << "Shutting down..." << std::endl; + c.adapter->getCommunicator()->shutdown(); +} diff --git a/gnuradio-core/src/lib/runtime/rpcserver_ice.h b/gnuradio-core/src/lib/runtime/rpcserver_ice.h new file mode 100644 index 0000000000..98847bbe05 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_ice.h @@ -0,0 +1,221 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCSERVER_ICE_H +#define RPCSERVER_ICE_H + +#include <rpcserver_base.h> +#include <rpcpmtconverters_ice.h> +#include <string> +#include <map> +#include <gnuradio.h> +#include <Ice/Exception.h> + +class rpcserver_ice : public virtual rpcserver_base, public GNURadio::ControlPort +{ +public: + rpcserver_ice(); + virtual ~rpcserver_ice(); + + void registerConfigureCallback(const std::string &id, const configureCallback_t callback); + void unregisterConfigureCallback(const std::string &id); + + void registerQueryCallback(const std::string &id, const queryCallback_t callback); + void unregisterQueryCallback(const std::string &id); + + virtual void set(const GNURadio::KnobMap&, const Ice::Current&); + + GNURadio::KnobMap get(const GNURadio::KnobIDList&, const Ice::Current&); + + GNURadio::KnobPropMap properties(const GNURadio::KnobIDList&, const Ice::Current&); + + virtual void shutdown(const Ice::Current&); + +private: + typedef std::map<std::string, configureCallback_t> ConfigureCallbackMap_t; + ConfigureCallbackMap_t d_setcallbackmap; + + typedef std::map<std::string, queryCallback_t> QueryCallbackMap_t; + QueryCallbackMap_t d_getcallbackmap; + + template<typename T, typename TMap> struct set_f + : public std::unary_function<T,void> + { + set_f(const Ice::Current& _c, TMap& _setcallbackmap, const priv_lvl_t& _cur_priv) : + c(_c), d_setcallbackmap(_setcallbackmap), cur_priv(_cur_priv) + {;} + + void operator()(const T& p) + { + ConfigureCallbackMap_t::const_iterator iter(d_setcallbackmap.find(p.first)); + if(iter != d_setcallbackmap.end()) { + if(cur_priv <= iter->second.priv) { + (*iter->second.callback).post(pmt::PMT_NIL, rpcpmtconverter::to_pmt(p.second,c)); + } + else { + std::cout << "Key " << p.first << " requires PRIVLVL <= " + << iter->second.priv << " to set, currently at: " + << cur_priv << std::endl; + } + } + else { + throw IceUtil::NullHandleException(__FILE__, __LINE__); + } + } + + const Ice::Current& c; + TMap& d_setcallbackmap; + const priv_lvl_t& cur_priv; + }; + + template<typename T, typename TMap> + struct get_f : public std::unary_function<T,void> + { + get_f(const Ice::Current& _c, TMap& _getcallbackmap, + const priv_lvl_t& _cur_priv, GNURadio::KnobMap& _outknobs) : + c(_c), d_getcallbackmap(_getcallbackmap), cur_priv(_cur_priv), outknobs(_outknobs) + {} + + void operator()(const T& p) + { + QueryCallbackMap_t::const_iterator iter(d_getcallbackmap.find(p)); + if(iter != d_getcallbackmap.end()) { + if(cur_priv <= iter->second.priv) { + outknobs[p] = rpcpmtconverter::from_pmt((*iter->second.callback).retrieve(), c); + } + else { + std::cout << "Key " << iter->first << " requires PRIVLVL: <= " + << iter->second.priv << " to get, currently at: " + << cur_priv << std::endl; + } + } + else { + throw IceUtil::NullHandleException(__FILE__, __LINE__); + } + } + + const Ice::Current& c; + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + GNURadio::KnobMap& outknobs; + }; + + template<typename T, typename TMap, typename TKnobMap> + struct get_all_f : public std::unary_function<T,void> + { + get_all_f(const Ice::Current& _c, TMap& _getcallbackmap, + const priv_lvl_t& _cur_priv, TKnobMap& _outknobs) : + c(_c), d_getcallbackmap(_getcallbackmap), cur_priv(_cur_priv), outknobs(_outknobs) + {;} + + void operator()(const T& p) + { + if(cur_priv <= p.second.priv) { + outknobs[p.first] = rpcpmtconverter::from_pmt(p.second.callback->retrieve(), c); + } + else { + std::cout << "Key " << p.first << " requires PRIVLVL <= " + << p.second.priv << " to get, currently at: " + << cur_priv << std::endl; + } + } + + const Ice::Current& c; + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + TKnobMap& outknobs; + }; + + template<typename T, typename TMap, typename TKnobMap> + struct properties_all_f : public std::unary_function<T,void> + { + properties_all_f(const Ice::Current& _c, QueryCallbackMap_t& _getcallbackmap, + const priv_lvl_t& _cur_priv, GNURadio::KnobPropMap& _outknobs) : + c(_c), d_getcallbackmap(_getcallbackmap), cur_priv(_cur_priv), outknobs(_outknobs) + {;} + + void operator()(const T& p) + { + if(cur_priv <= p.second.priv) { + GNURadio::KnobProp prop;//(new GNURadio::KnobProp()); + prop.type = GNURadio::KNOBDOUBLE; + prop.units = p.second.units; + prop.description = p.second.description; + prop.min = rpcpmtconverter::from_pmt(p.second.min, c); + prop.max = rpcpmtconverter::from_pmt(p.second.max, c); + prop.display = static_cast<GNURadio::DisplayType>(p.second.display); + outknobs[p.first] = prop; + } + else { + std::cout << "Key " << p.first << " requires PRIVLVL <= " + << p.second.priv << " to get, currently at: " + << cur_priv << std::endl; + } + } + + const Ice::Current& c; + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + TKnobMap& outknobs; + }; + + template<class T, typename TMap, typename TKnobMap> + struct properties_f : public std::unary_function<T,void> + { + properties_f(const Ice::Current& _c, TMap& _getcallbackmap, + const priv_lvl_t& _cur_priv, TKnobMap& _outknobs) : + c(_c), d_getcallbackmap(_getcallbackmap), cur_priv(_cur_priv), outknobs(_outknobs) + {;} + + void operator()(const T& p) + { + typename TMap::const_iterator iter(d_getcallbackmap.find(p)); + if(iter != d_getcallbackmap.end()) { + if(cur_priv <= iter->second.priv) { + GNURadio::KnobProp prop; + prop.type = GNURadio::KNOBDOUBLE; + prop.units = iter->second.units; + prop.description = iter->second.description; + prop.min = rpcpmtconverter::from_pmt(iter->second.min, c); + prop.max = rpcpmtconverter::from_pmt(iter->second.max, c); + prop.display = static_cast<GNURadio::DisplayType>(iter->second.display); + //outknobs[iter->first] = prop; + outknobs[p] = prop; + } + else { + std::cout << "Key " << iter->first << " requires PRIVLVL: <= " << + iter->second.priv << " to get, currently at: " << cur_priv << std::endl; + } + } + else { + throw IceUtil::NullHandleException(__FILE__, __LINE__); + } + } + + const Ice::Current& c; + TMap& d_getcallbackmap; + const priv_lvl_t& cur_priv; + TKnobMap& outknobs; + }; +}; + +#endif /* RPCSERVER_ICE_H */ diff --git a/gnuradio-core/src/lib/runtime/rpcserver_resource.cc b/gnuradio-core/src/lib/runtime/rpcserver_resource.cc new file mode 100644 index 0000000000..fbc83d05a5 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_resource.cc @@ -0,0 +1,126 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcserver_resource.h> +#include <rpcpmtconverters_resource.h> +#include <iostream> + +rpcserver_resource::rpcserver_resource() +{;} + +rpcserver_resource::~rpcserver_resource() +{;} + +char* +rpcserver_resource::identifier() +{ +#warning "Code missing in function <char* rpcserver_resource::identifier()>" +} + +void +rpcserver_resource::start() +{ +#warning "Code missing in function <void rpcserver_resource::start()>" +} + +void +rpcserver_resource::stop() +{ +#warning "Code missing in function <void rpcserver_resource::stop()>" +} + +void +rpcserver_resource::initialize() +{ +#warning "Code missing in function <void rpcserver_resource::initialize()>" +} + +void +rpcserver_resource::releaseObject() +{ +#warning "Code missing in function <void rpcserver_resource::releaseObject()>" +} + +void +rpcserver_resource::runTest(::CORBA::ULong testid, CF::Properties& testValues) +{ +#warning "Code missing in function <void rpcserver_resource::runTest(::CORBA::ULong testid, CF::Properties& testValues)>" +} + +void +rpcserver_resource::registerConfigureCallback(const std::string &id, const configureCallback_t callback) +{ + std::cout << "rpcserver_resource::registering: " << id << std::endl; + d_callbackmap.insert(ConfigureCallbackMap_t::value_type(id, callback)); +} + +void +rpcserver_resource::configure(const CF::Properties& configProperties) +{ + for(unsigned int i=0;i<configProperties.length();++i) { + std::string id((const char*) configProperties[i].id); + ConfigureCallbackMap_t::const_iterator iter = d_setcallbackmap.begin(); + iter = d_setcallbackmap.find(id); + if(iter != d_setcallbackmap.end()) { + (*iter->second).post(rpcpmtconverter::to_pmt(configProperties[i].value)); + } + else { + throw IceUtil::NullHandleException(__FILE__, __LINE__); + } + } +} + +//template<class T> struct rpcserver_resource::extractQuery : public std::unary_function<T, void> { +// void operator()(T& x) { +// x++; +// } +//}; + +void +rpcserver_resource::query(CF::Properties& configProperties) +{ + if(configProperties.length() == 0) { + //std::for_each(d_getcallbackmap.begin(), d_getcallbackmap.end(), extractQuery<getCallback_t>()); + configProperties.length(d_getcallbackmap.size()); + + } + else { + for(unsigned int i=0;i<configProperties.length();++i) { + std::string id((const char*) configProperties[i].id); + QueryCallbackMap_t::const_iterator iter = d_getcallbackmap.begin(); + iter = d_getcallbackmap.find(id); + if (iter != d_getcallbackmap.end()) { + //(*iter->second).post(rpcpmtconverter::to_pmt(configProperties[i].value)); + = from_pmt((*iter->second.callback).retrieve(), c); + } + else { + throw IceUtil::NullHandleException(__FILE__, __LINE__); + } + } + } +} + +CORBA::Object_ptr +rpcserver_resource::getPort(const char* name) +{ +#warning "Code missing in function <CORBA::Object_ptr rpcserver_resource::getPort(const char* name)>" +} diff --git a/gnuradio-core/src/lib/runtime/rpcserver_selector.cc b/gnuradio-core/src/lib/runtime/rpcserver_selector.cc new file mode 100644 index 0000000000..362d5f060a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_selector.cc @@ -0,0 +1,40 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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. + */ + +#include <rpcserver_booter_aggregator.h> +#include <rpcmanager.h> +#include <rpcserver_selector.h> + +bool rpcmanager::make_aggregator(false); + +#ifdef RPCSERVER_ICE + #include <rpcserver_booter_ice.h> + rpcmanager::rpcserver_booter_register_helper<rpcserver_booter_ice> boot_ice; +#endif + +#ifdef RPCSERVER_ERLANG + #error TODO ERLANG +#endif + +#ifdef RPCSERVER_XMLRPC + #error TODO XMLRPC +#endif diff --git a/gnuradio-core/src/lib/runtime/rpcserver_selector.h b/gnuradio-core/src/lib/runtime/rpcserver_selector.h new file mode 100644 index 0000000000..fa63c9a2dc --- /dev/null +++ b/gnuradio-core/src/lib/runtime/rpcserver_selector.h @@ -0,0 +1,32 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 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 RPCSERVER_SELECTOR +#define RPCSERVER_SELECTOR + +#define RPCSERVER_ENABLED + +#define RPCSERVER_ICE +//#define RPCSERVER_ERLANG +//#define RPCSERVER_XMLRPC + +#endif diff --git a/gnuradio-core/src/lib/runtime/runtime.i b/gnuradio-core/src/lib/runtime/runtime.i index 8e35df8342..f9945e8060 100644 --- a/gnuradio-core/src/lib/runtime/runtime.i +++ b/gnuradio-core/src/lib/runtime/runtime.i @@ -67,3 +67,73 @@ %include <gr_sync_decimator.i> %include <gr_sync_interpolator.i> %include <gr_top_block.i> + + +#ifdef GR_CTRLPORT + +enum DisplayType { + DISPNULL, + DISPTIMESERIESF, + DISPTIMESERIESC, + DISPXYSCATTER, + DISPXYLINE +}; + +enum priv_lvl_t { + RPC_PRIVLVL_ALL = 0, + RPC_PRIVLVL_MIN = 9, + RPC_PRIVLVL_NONE = 10 +}; + +enum KnobType { + KNOBBOOL, KNOBCHAR, KNOBINT, KNOBFLOAT, + KNOBDOUBLE, KNOBSTRING, KNOBLONG, KNOBVECBOOL, + KNOBVECCHAR, KNOBVECINT, KNOBVECFLOAT, KNOBVECDOUBLE, + KNOBVECSTRING, KNOBVECLONG +}; + +%template(StrVector) std::vector<std::string>; + +%{ +#include <rpcserver_booter_base.h> +#include <rpcserver_booter_aggregator.h> +#include <pycallback_object.h> +%} + +%include <rpcserver_booter_base.h> +%include <rpcserver_booter_aggregator.h> +%include <pycallback_object.h> + +// Declare this class here but without the nested templated class +// inside (replaces include of rpcmanager.h) +class GR_CORE_API rpcmanager : public virtual rpcmanager_base +{ + public: + rpcmanager(); + ~rpcmanager(); + + static rpcserver_booter_base* get(); + + static void register_booter(rpcserver_booter_base* booter); +}; + + +// Attach a new python callback method to Python function +%extend pycallback_object { + // Set a Python function object as a callback function + // Note : PyObject *pyfunc is remapped with a typempap + void activate(PyObject *pyfunc) + { + self->set_callback(pyfunc); + Py_INCREF(pyfunc); + } +} + +%template(RPC_get_string) pycallback_object<std::string>; +%template(RPC_get_int) pycallback_object<int>; +%template(RPC_get_float) pycallback_object<float>; +%template(RPC_get_double) pycallback_object<double>; +%template(RPC_get_vector_float) pycallback_object<std::vector<float> >; +%template(RPC_get_vector_gr_complex) pycallback_object<std::vector<gr_complex> >; + +#endif /* GR_CTRLPORT */ diff --git a/gnuradio-core/src/lib/swig/CMakeLists.txt b/gnuradio-core/src/lib/swig/CMakeLists.txt index e47771570b..6a989616d1 100644 --- a/gnuradio-core/src/lib/swig/CMakeLists.txt +++ b/gnuradio-core/src/lib/swig/CMakeLists.txt @@ -31,6 +31,13 @@ set(GR_SWIG_INCLUDE_DIRS ) set(GR_SWIG_LIBRARIES gnuradio-core) +if(ENABLE_GR_CTRLPORT) + list(APPEND GR_SWIG_FLAGS -DGR_CTRLPORT) + list(APPEND GR_SWIG_LIBRARIES + ${ICE_LIBRARIES} + ) +endif(ENABLE_GR_CTRLPORT) + ######################################################################## # Build and install the swig targets ######################################################################## @@ -41,7 +48,8 @@ set(GR_SWIG_LIBRARIES gnuradio-core) # X86_64, g++'s resident set size was 650MB! # ---------------------------------------------------------------- -set(GR_SWIG_TARGET_DEPS general_generated gengen_generated filter_generated pmt_swig) +set(GR_SWIG_TARGET_DEPS gnuradio_core_generated_sources + general_generated gengen_generated filter_generated pmt_swig) foreach(what runtime general gengen io) SET(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${what}_swig_doc.i) diff --git a/gnuradio-core/src/python/gnuradio/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/CMakeLists.txt index bf696e0d34..ab46dbb92a 100644 --- a/gnuradio-core/src/python/gnuradio/CMakeLists.txt +++ b/gnuradio-core/src/python/gnuradio/CMakeLists.txt @@ -24,6 +24,7 @@ add_subdirectory(gru) add_subdirectory(gruimpl) add_subdirectory(blks2) add_subdirectory(blks2impl) +add_subdirectory(ctrlport) GR_PYTHON_INSTALL(FILES __init__.py diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/ctrlport/CMakeLists.txt new file mode 100644 index 0000000000..1268030ebb --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/CMakeLists.txt @@ -0,0 +1,129 @@ +# Copyright 2012 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. + +######################################################################## +include(GrPython) + +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/../../gnuradio_ice.py + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/__init__.py + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/Booter/__init__.py + COMMAND ${ICE_SLICE2PY} -I${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/runtime + --output-dir=${CMAKE_CURRENT_BINARY_DIR}/../../ + ${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/runtime/gnuradio.ice + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/runtime/gnuradio.ice + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Slicing gnuradio.slice" +) + +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/frontend_ice.py + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/Frontend/__init__.py + COMMAND ${ICE_SLICE2PY} -I${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/runtime + --output-dir=${CMAKE_CURRENT_BINARY_DIR}/../../ + ${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/runtime/frontend.ice + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../../lib/runtime/frontend.ice + ${CMAKE_CURRENT_BINARY_DIR}/../../gnuradio_ice.py + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Slicing frontend.slice" +) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py + ${CMAKE_CURRENT_SOURCE_DIR}/IceRadioClient.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/ + COMPONENT "core_python" +) + +# We don't want to install these in the root Python directory, but we +# aren't given a choice based on the way slice2py generates the +# information. +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/../../gnuradio_ice.py + ${CMAKE_CURRENT_BINARY_DIR}/../../frontend_ice.py + DESTINATION ${GR_PYTHON_DIR} + COMPONENT "core_python" +) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/__init__.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/GNURadio + COMPONENT "core_python" +) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/Booter/__init__.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/GNURadio/Booter + COMPONENT "core_python" +) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/GNURadio/Frontend/__init__.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/GNURadio/Frontend + COMPONENT "core_python" +) + +install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/icon.png + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport + COMPONENT "core_python" +) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/DataPlotter.py + ${CMAKE_CURRENT_SOURCE_DIR}/GrDataPlotter.py + ${CMAKE_CURRENT_SOURCE_DIR}/monitor.py + DESTINATION ${GR_PYTHON_DIR}/gnuradio/ctrlport/ + COMPONENT "core_python" +) + +GR_PYTHON_INSTALL( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/ctrlport-monitor + ${CMAKE_CURRENT_SOURCE_DIR}/gr-ctrlport-monitor + DESTINATION ${GR_RUNTIME_DIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE + COMPONENT "core_python" +) + +######################################################################## +# Handle the unit tests +######################################################################## +if(ENABLE_GR_CTRLPORT) + if(ENABLE_TESTING) + include(GrTest) + file(GLOB py_qa_test_files "qa_*.py") + foreach(py_qa_test_file ${py_qa_test_files}) + get_filename_component(py_qa_test_name ${py_qa_test_file} NAME_WE) + set(GR_TEST_PYTHON_DIRS + ${CMAKE_BINARY_DIR}/gnuradio-core/src/python + ${CMAKE_BINARY_DIR}/gnuradio-core/src/lib/swig + ) + set(GR_TEST_TARGET_DEPS gruel gnuradio-core) + GR_ADD_TEST(${py_qa_test_name} ${PYTHON_EXECUTABLE} ${py_qa_test_file}) + endforeach(py_qa_test_file) + endif(ENABLE_TESTING) +endif(ENABLE_GR_CTRLPORT) diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/DataPlotter.py b/gnuradio-core/src/python/gnuradio/ctrlport/DataPlotter.py new file mode 100644 index 0000000000..c689bfa055 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/DataPlotter.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +import sys + +try: + from PyQt4.QtCore import Qt; + from PyQt4 import QtGui, QtCore +except ImportError: + print "Error: Program requires PyQt4." + sys.exit(1) + +try: + import PyQt4.Qwt5 as Qwt + from PyQt4.Qwt5.anynumpy import * +except ImportError: + print "Error: Program requires PyQwt5." + sys.exit(1) + + +class Zoomer(Qwt.QwtPlotZoomer): + def __init__(self, a,b,c,d,e): + Qwt.QwtPlotZoomer.__init__(self,a,b,c,d,e); + self.zoomers = []; + + def zoom(self, r): + Qwt.QwtPlotZoomer.zoom(self,r); + if(r == 0): + #self.plot().setAxisAutoScale(True); + self.plot().setAxisAutoScale(Qwt.QwtPlot.xBottom) + self.plot().setAxisAutoScale(Qwt.QwtPlot.yLeft) + self.plot().replot(); + + +class DataPlotterBase(Qwt.QwtPlot): + DefaultColors = ( Qt.green, Qt.red, Qt.blue, + Qt.cyan, Qt.magenta, Qt.black, Qt.darkRed, + Qt.darkGray, Qt.darkGreen, Qt.darkBlue, Qt.yellow) + + dropSignal = QtCore.pyqtSignal(QtCore.QEvent) + + def contextMenuEvent(self,e): + menu = QtGui.QMenu(self); + menu.addAction(self.gridAct); + menu.addAction(self.axesAct); + menu.addAction(self.curvAct); + menu.exec_(e.globalPos()); + + def dragEnterEvent(self,e): + e.accept(); + + def dropEvent(self, e): + Qwt.QwtPlot.dropEvent(self,e) + self.dropSignal.emit( e ); + + + + def __init__(self, parent, title, xlabel, ylabel, size, x, y): + Qwt.QwtPlot.__init__(self, parent) + self.callback = None; + + self.gridAct = QtGui.QAction("Toggle &Grid", self, triggered=self.toggleGrid); + self.axesAct = QtGui.QAction("Toggle &Axes", self, triggered=self.toggleAxes); + self.curvAct = QtGui.QAction("Toggle &Lines", self, triggered=self.toggleCurve); + + # Set up the zoomer + self.zoomer = Zoomer(Qwt.QwtPlot.xBottom, + Qwt.QwtPlot.yLeft, + Qwt.QwtPicker.DragSelection, + Qwt.QwtPicker.AlwaysOff, + self.canvas()) + + # Crosshairs + Data labels + self.picker = Qwt.QwtPlotPicker( + Qwt.QwtPlot.xBottom, + Qwt.QwtPlot.yLeft, + Qwt.QwtPicker.PointSelection | Qwt.QwtPicker.DragSelection, + Qwt.QwtPlotPicker.CrossRubberBand, + Qwt.QwtPicker.AlwaysOn, + self.canvas()) + self.picker.setRubberBandPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.DashLine)) + self.picker.setTrackerPen(QtGui.QPen(QtCore.Qt.white)) + + self.axisEnable = False; + # Turn off bloated data labels + self.enableAxis(Qwt.QwtPlot.yLeft, False); + self.enableAxis(Qwt.QwtPlot.xBottom, False); + + # Allow panning with middle mouse + panner = Qwt.QwtPlotPanner(self.canvas()) + panner.setAxisEnabled(Qwt.QwtPlot.yRight, False) + panner.setMouseButton(Qt.MidButton) + + # Accept dropping of stats + self.setAcceptDrops(True); + self.grid = None + self.curve_en = False + self.setCanvasBackground(Qt.black) + + self.insertLegend(Qwt.QwtLegend(), Qwt.QwtPlot.TopLegend); + + self.axisEnabled(True); + + self.resize(size, size) + self.setAutoReplot(False) + self.show() + self.updateTimerInt = 500 + self.startTimer(self.updateTimerInt) + + # Set Axis on and Grid off by default + #self.toggleGrid(); + self.toggleAxes(); + + + def toggleAxes(self): + self.axisEnable = not self.axisEnable; + self.enableAxis(Qwt.QwtPlot.yLeft, self.axisEnable); + self.enableAxis(Qwt.QwtPlot.xBottom, self.axisEnable); + + + def toggleGrid(self): + # grid + if self.grid == None: + self.grid = Qwt.QwtPlotGrid() + self.grid.enableXMin(True) + self.grid.setMajPen(QtGui.QPen(Qt.gray, 0, Qt.DotLine)) + self.grid.setMinPen(QtGui.QPen(Qt.gray, 0 , Qt.DotLine)) + self.grid.attach(self) + else: + self.grid.detach() + self.grid = None + + return self + + def toggleCurve(self): + self.curve_en = not self.curve_en; + + +class DataPlotterVector(DataPlotterBase): + def __init__(self, parent, legend, title, xlabel, ylabel, size, x, y): + DataPlotterBase.__init__(self, parent, title, xlabel, ylabel, size, x, y) + self.curve = Qwt.QwtPlotCurve(legend) + self.curve.attach(self) + self.tag = None; + self.x = self.y = [0.0]; + + def offerData(self, data, tag): + if(tag == self.tag): + self.x = data[::2]; self.y = data[1::2] + + def timerEvent(self, e): + if(self.curve_en): + self.curve.setStyle(Qwt.QwtPlotCurve.Lines) + self.curve.setPen(QtGui.QPen(Qt.green)) + else: + self.curve.setStyle(Qwt.QwtPlotCurve.NoCurve) + + self.curve.setData(self.x, self.y) + self.replot() + + def enableLegend(self): + self.insertLegend(Qwt.QwtLegend(), Qwt.QwtPlot.BottomLegend); + return self + + def setSeries(self,tag,name): + self.tag = tag; + self.curve.setTitle(name) + + +class DataPlotterVectorOne(DataPlotterVector): + def __init__(self, parent, legend, title, xlabel, ylabel, size, x, y): + DataPlotterVector.__init__(self, parent, legend, title, xlabel, ylabel, size, x, y) + self.curve.setSymbol(Qwt.QwtSymbol(Qwt.QwtSymbol.XCross, + QtGui.QBrush(), QtGui.QPen(Qt.green), QtCore.QSize(2, 2))) + self.setAxisAutoScale(True) + self.axisSet = False; + + # Lines on by default + self.toggleCurve(); + + def offerData(self, data, tag): + if(tag == self.tag): + if not self.axisSet: + self.setAxisScale(1, 0, len(data)); + self.axisSet = True; + self.x = range(0,len(data)); + self.y = data; + + +class DataPlotterConst(DataPlotterVector): + def __init__(self, parent, legend, title, xlabel, ylabel, size, x, y): + DataPlotterVector.__init__(self, parent, legend, title, xlabel, ylabel, size, x, y) + self.x = arange(-2, 100.1, 2) + self.y = zeros(len(self.x), Float) + self.curve.setSymbol(Qwt.QwtSymbol(Qwt.QwtSymbol.XCross, + QtGui.QBrush(), QtGui.QPen(Qt.green), QtCore.QSize(2, 2))) + self.curve.setStyle(Qwt.QwtPlotCurve.NoCurve) + self.setAxisAutoScale(False) + +class DataPlotterEqTaps(DataPlotterVector): + def __init__(self, parent, legend, title, xlabel, ylabel, size, x, y, qtcolor): + DataPlotterVector.__init__(self, parent, legend, title, xlabel, ylabel, size, x, y) + self.x = arange(-.5, .5, 1) + self.y = zeros(len(self.x), Float) + self.curve.setPen(QtGui.QPen(qtcolor)) + +class DataPlotterTicker(DataPlotterBase): + def __init__(self, parent, title, xlabel, ylabel, size, x, y, seconds = 60): + DataPlotterBase.__init__(self, parent, title, xlabel, ylabel, size, x, y) + self.series = {} + self.setTimeScale(seconds) + +# AAAAAAAAAA - enable for legend at bottom +# self.insertLegend(Qwt.QwtLegend(), Qwt.QwtPlot.BottomLegend); + self.skipEvents=1 + + def setTimeScale(self, seconds): + intv = float(self.updateTimerInt) / 1000 + self.x = arange(0, seconds, intv) + #self.x = arange(0, seconds, 1) + return self + + def addSeries(self, tag, label, qtcolor = None, alpha = 1): + class Series: + def __init__(self, tag, label, qtcolor, x, plot): + self.vec = zeros(len(x), Float) + self.value = None + self.alpha = alpha + self.curve = Qwt.QwtPlotCurve(label) + self.curve.setPen(QtGui.QPen(qtcolor)) + self.plot = plot + + if qtcolor == None: qtcolor = self.DefaultColors[len(self.series)] + self.series[tag] = s = Series(tag, label, qtcolor, self.x, self) + self.enableSeries(tag) + return self + + def enableSeries(self, tag): + if self.hasSeries(tag): + s = self.series[tag] + s.enabled = True + s.curve.attach(s.plot) + return self + + def disableSeries(self, tag): + if self.hasSeries(tag): + s = self.series[tag] + s.enabled = False + s.curve.detach() + return self + + def toggleSeries(self,tag): + if self.seriesIsEnabled(tag): + self.disableSeries(tag) + else: + self.enableSeries(tag) + return self + + def timerEvent(self, e): + for k, v in self.series.iteritems(): + if v.value == None: continue + elif v.vec[0] == 0: v.vec = ones(len(v.vec), Float)*v.value + + prev = v.vec[0] + v.vec = concatenate((v.vec[:1], v.vec[:-1]), 1) + v.vec[0] = v.value*v.alpha + prev*(1-v.alpha) + self.series[k] = v + v.curve.setData(self.x, v.vec) + self.replot() + + def offerData(self, value, tag): + if(self.series.has_key(tag)): + if not value == NaN: + self.series[tag].value = value + #print "Data Offer %s items:"%(tag) + return self + + def hasSeries(self, tag): + return self.series.has_key(tag); + + def seriesIsEnabled(self, tag): + if self.hasSeries(tag): return self.series[tag].enabled + else: return False + +class DataPlotterTickerWithSeriesButtons(DataPlotterTicker): + def __init__(self, parent, title, xlabel, ylabel, size, x, y, seconds = 60): + DataPlotterTicker.__init__(self, parent, title, xlabel, ylabel, size, x, y, seconds) + DataPlotterTicker.setAcceptDrops(self,True); + self.buttonx = 50; self.buttony = 20; self.buttonSize = 16 + self.btns = [] + + + + + def addSeriesWithButton(self, tag, legend, qtcolor=None, alpha = 1): + self.addSeries(tag, legend, qtcolor, alpha) + lenbtns = len(self.btns) + + btn = QtGui.QToolButton(self) + btn.rank = lenbtns + btn.setText(str(btn.rank)) + btn.tag = tag + #btn.setIcon(Qt.QIcon(Qt.QPixmap(print_xpm))) + #btn.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) + #btn.setForegroundColor(Qt.red) + self.btns.append(btn) + btn.setGeometry(self.buttonx, self.buttony, self.buttonSize, self.buttonSize) + self.buttonx += self.buttonSize + + if lenbtns == 0: callback = self.print_0 + if lenbtns == 1: callback = self.print_1 + if lenbtns == 2: callback = self.print_2 + if lenbtns == 3: callback = self.print_3 + if lenbtns == 4: callback = self.print_4 + if lenbtns == 5: callback = self.print_5 + if lenbtns == 6: callback = self.print_6 + if lenbtns == 7: callback = self.print_7 + self.connect(btn, QtCore.SIGNAL('clicked()'), callback) + return self + + def toggleSeriesWithButton(self,btn): + DataPlotterTicker.toggleSeries(self, btn.tag) + + if self.seriesIsEnabled(btn.tag): + # btn.setForegroundRole(Qt.QPalette.NoRole) + btn.setText(str(btn.rank)) + else: + btn.setText('x') + # btn.setForegroundRole(Qt.QPalette.Light) + + def print_0(self): self.toggleSeriesWithButton(self.btns[0]) + def print_1(self): self.toggleSeriesWithButton(self.btns[1]) + def print_2(self): self.toggleSeriesWithButton(self.btns[2]) + def print_3(self): self.toggleSeriesWithButton(self.btns[3]) + def print_4(self): self.toggleSeriesWithButton(self.btns[4]) + def print_5(self): self.toggleSeriesWithButton(self.btns[5]) + def print_6(self): self.toggleSeriesWithButton(self.btns[6]) + def print_7(self): self.toggleSeriesWithButton(self.btns[7]) + +class DataPlotterValueTable: + def __init__(self, parent, x, y, xsize, ysize, headers=['Statistic Key ( Source Block :: Stat Name ) ', 'Curent Value', 'Units', 'Description']): + # must encapsulate, cuz Qt's bases are not classes + self.treeWidget = QtGui.QTreeWidget(parent) + self.treeWidget.setColumnCount(len(headers)) + self.treeWidget.setGeometry(x,y,xsize,ysize) + self.treeWidget.setHeaderLabels(headers) + self.treeWidget.resizeColumnToContents(0) + + def updateItems(self, knobs, knobprops): + # save previous selection if exists + sel = self.treeWidget.currentItem(); + row = self.treeWidget.indexOfTopLevelItem(sel); + items = []; + self.treeWidget.clear() + for k, v in knobs.iteritems(): + items.append(QtGui.QTreeWidgetItem([str(k), str(v.value), knobprops[k].units, knobprops[k].description])) + self.treeWidget.insertTopLevelItems(0, items) + # re-set previous selection if exists + if(row != -1): + try: + self.treeWidget.setCurrentItem(self.treeWidget.topLevelItem(row)); + except: + pass diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/GrDataPlotter.py b/gnuradio-core/src/python/gnuradio/ctrlport/GrDataPlotter.py new file mode 100644 index 0000000000..f33160aca2 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/GrDataPlotter.py @@ -0,0 +1,423 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +from gnuradio import gr +import sys, time + +try: + from gnuradio import qtgui + from PyQt4 import QtGui, QtCore + import sip +except ImportError: + print "Error: Program requires PyQt4 and gr-qtgui." + sys.exit(1) + +class GrDataPlotterC(gr.top_block): + def __init__(self, name, rate, pmin=None, pmax=None): + gr.top_block.__init__(self) + + self._name = name + self._npts = 500 + samp_rate = 1.0 + + self._last_data = self._npts*[0,] + self._data_len = 0 + + self.src = gr.vector_source_c([]) + self.thr = gr.throttle(gr.sizeof_gr_complex, rate) + self.snk = qtgui.time_sink_c(self._npts, samp_rate, + self._name, 1) + + if(pmin is not None or not pmax is None): + self.snk.set_y_axis(pmin, pmax) + + self.connect(self.src, self.thr, (self.snk, 0)) + + self.snk.set_line_label(0, "Real") + self.snk.set_line_label(1, "Imag") + + self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget) + + def __del__(self): + pass + + def qwidget(self): + return self.py_window + + def name(self): + return self._name + + def update(self, data): + # Ask GUI if there has been a change in nsamps + npts = self.snk.nsamps() + if(self._npts != npts): + + # Adjust buffers to accomodate new settings + if(npts < self._npts): + if(self._data_len < npts): + self._last_data = self._last_data[0:npts] + else: + self._last_data = self._last_data[self._data_len-npts:self._data_len] + self._data_len = npts + else: + self._last_data += (npts - self._npts)*[0,] + self._npts = npts + self.snk.reset() + + # Update the plot data depending on type + if(type(data) == list): + data_r = data[0::2] + data_i = data[1::2] + data = [complex(r,i) for r,i in zip(data_r, data_i)] + if(len(data) > self._npts): + self.src.set_data(data) + self._last_data = data[-self._npts:] + else: + newdata = self._last_data[-(self._npts-len(data)):] + newdata += data + self.src.set_data(newdata) + self._last_data = newdata + + else: # single value update + if(self._data_len < self._npts): + self._last_data[self._data_len] = data + self._data_len += 1 + else: + self._last_data = self._last_data[1:] + self._last_data.append(data) + self.src.set_data(self._last_data) + +class GrDataPlotterF(gr.top_block): + def __init__(self, name, rate, pmin=None, pmax=None): + gr.top_block.__init__(self) + + self._name = name + self._npts = 500 + samp_rate = 1.0 + + self._last_data = self._npts*[0,] + self._data_len = 0 + + self.src = gr.vector_source_f([]) + self.thr = gr.throttle(gr.sizeof_float, rate) + self.snk = qtgui.time_sink_f(self._npts, samp_rate, + self._name, 1) + + if(pmin is not None or not pmax is None): + self.snk.set_y_axis(pmin, pmax) + + self.connect(self.src, self.thr, (self.snk, 0)) + + self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget) + + def __del__(self): + pass + + def qwidget(self): + return self.py_window + + def name(self): + return self._name + + def update(self, data): + # Ask GUI if there has been a change in nsamps + npts = self.snk.nsamps() + if(self._npts != npts): + + # Adjust buffers to accomodate new settings + if(npts < self._npts): + if(self._data_len < npts): + self._last_data = self._last_data[0:npts] + else: + self._last_data = self._last_data[self._data_len-npts:self._data_len] + self._data_len = npts + else: + self._last_data += (npts - self._npts)*[0,] + self._npts = npts + self.snk.reset() + + # Update the plot data depending on type + if(type(data) == list): + if(len(data) > self._npts): + self.src.set_data(data) + self._last_data = data[-self._npts:] + else: + newdata = self._last_data[-(self._npts-len(data)):] + newdata += data + self.src.set_data(newdata) + self._last_data = newdata + + else: # single value update + if(self._data_len < self._npts): + self._last_data[self._data_len] = data + self._data_len += 1 + else: + self._last_data = self._last_data[1:] + self._last_data.append(data) + self.src.set_data(self._last_data) + + +class GrDataPlotterConst(gr.top_block): + def __init__(self, name, rate, pmin=None, pmax=None): + gr.top_block.__init__(self) + + self._name = name + self._npts = 500 + samp_rate = 1.0 + + self._last_data = self._npts*[0,] + self._data_len = 0 + + self.src = gr.vector_source_c([]) + self.thr = gr.throttle(gr.sizeof_gr_complex, rate) + self.snk = qtgui.const_sink_c(self._npts, + self._name, 1) + + if(pmin is not None or not pmax is None): + self.snk.set_x_axis(pmin, pmax) + self.snk.set_y_axis(pmin, pmax) + + self.connect(self.src, self.thr, (self.snk, 0)) + + self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget) + + def __del__(self): + pass + + def qwidget(self): + return self.py_window + + def name(self): + return self._name + + def update(self, data): + # Ask GUI if there has been a change in nsamps + npts = self.snk.nsamps() + if(self._npts != npts): + + # Adjust buffers to accomodate new settings + if(npts < self._npts): + if(self._data_len < npts): + self._last_data = self._last_data[0:npts] + else: + self._last_data = self._last_data[self._data_len-npts:self._data_len] + self._data_len = npts + else: + self._last_data += (npts - self._npts)*[0,] + self._npts = npts + self.snk.reset() + + # Update the plot data depending on type + if(type(data) == list): + data_r = data[0::2] + data_i = data[1::2] + data = [complex(r,i) for r,i in zip(data_r, data_i)] + if(len(data) > self._npts): + self.src.set_data(data) + self._last_data = data[-self._npts:] + else: + newdata = self._last_data[-(self._npts-len(data)):] + newdata += data + self.src.set_data(newdata) + self._last_data = newdata + + else: # single value update + if(self._data_len < self._npts): + self._last_data[self._data_len] = data + self._data_len += 1 + else: + self._last_data = self._last_data[1:] + self._last_data.append(data) + self.src.set_data(self._last_data) + + +class GrDataPlotterPsdC(gr.top_block): + def __init__(self, name, rate, pmin=None, pmax=None): + gr.top_block.__init__(self) + + self._name = name + self._samp_rate = 1.0 + self._fftsize = 2048 + self._wintype = gr.firdes.WIN_BLACKMAN_hARRIS + self._fc = 0 + + self._last_data = self._fftsize*[0,] + self._data_len = 0 + + self.src = gr.vector_source_c([]) + self.thr = gr.throttle(gr.sizeof_gr_complex, rate) + self.snk = qtgui.freq_sink_c(self._fftsize, self._wintype, + self._fc, self._samp_rate, + self._name, 1) + + if(pmin is not None or not pmax is None): + self.snk.set_y_axis(pmin, pmax) + + self.connect(self.src, self.thr, (self.snk, 0)) + + self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget) + + def __del__(self): + pass + + def qwidget(self): + return self.py_window + + def name(self): + return self._name + + def update(self, data): + # Ask GUI if there has been a change in nsamps + fftsize = self.snk.fft_size() + if(self._fftsize != fftsize): + + # Adjust buffers to accomodate new settings + if(fftsize < self._fftsize): + if(self._data_len < fftsize): + self._last_data = self._last_data[0:fftsize] + else: + self._last_data = self._last_data[self._data_len-fftsize:self._data_len] + self._data_len = fftsize + else: + self._last_data += (fftsize - self._fftsize)*[0,] + self._fftsize = fftsize + self.snk.reset() + + # Update the plot data depending on type + if(type(data) == list): + data_r = data[0::2] + data_i = data[1::2] + data = [complex(r,i) for r,i in zip(data_r, data_i)] + if(len(data) > self._fftsize): + self.src.set_data(data) + self._last_data = data[-self._fftsize:] + else: + newdata = self._last_data[-(self._fftsize-len(data)):] + newdata += data + self.src.set_data(newdata) + self._last_data = newdata + + else: # single value update + if(self._data_len < self._fftsize): + self._last_data[self._data_len] = data + self._data_len += 1 + else: + self._last_data = self._last_data[1:] + self._last_data.append(data) + self.src.set_data(self._last_data) + +class GrDataPlotterPsdF(gr.top_block): + def __init__(self, name, rate, pmin=None, pmax=None): + gr.top_block.__init__(self) + + self._name = name + self._samp_rate = 1.0 + self._fftsize = 2048 + self._wintype = gr.firdes.WIN_BLACKMAN_hARRIS + self._fc = 0 + + self._last_data = self._fftsize*[0,] + self._data_len = 0 + + self.src = gr.vector_source_f([]) + self.thr = gr.throttle(gr.sizeof_float, rate) + self.snk = qtgui.freq_sink_f(self._fftsize, self._wintype, + self._fc, self._samp_rate, + self._name, 1) + + if(pmin is not None or not pmax is None): + self.snk.set_y_axis(pmin, pmax) + + self.connect(self.src, self.thr, (self.snk, 0)) + + self.py_window = sip.wrapinstance(self.snk.pyqwidget(), QtGui.QWidget) + + def __del__(self): + pass + + def qwidget(self): + return self.py_window + + def name(self): + return self._name + + def update(self, data): + # Ask GUI if there has been a change in nsamps + fftsize = self.snk.fft_size() + if(self._fftsize != fftsize): + + # Adjust buffers to accomodate new settings + if(fftsize < self._fftsize): + if(self._data_len < fftsize): + self._last_data = self._last_data[0:fftsize] + else: + self._last_data = self._last_data[self._data_len-fftsize:self._data_len] + self._data_len = fftsize + else: + self._last_data += (fftsize - self._fftsize)*[0,] + self._fftsize = fftsize + self.snk.reset() + + # Update the plot data depending on type + if(type(data) == list): + data_r = data[0::2] + data_i = data[1::2] + data = [complex(r,i) for r,i in zip(data_r, data_i)] + if(len(data) > self._fftsize): + self.src.set_data(data) + self._last_data = data[-self._fftsize:] + else: + newdata = self._last_data[-(self._fftsize-len(data)):] + newdata += data + self.src.set_data(newdata) + self._last_data = newdata + + else: # single value update + if(self._data_len < self._fftsize): + self._last_data[self._data_len] = data + self._data_len += 1 + else: + self._last_data = self._last_data[1:] + self._last_data.append(data) + self.src.set_data(self._last_data) + + +class GrDataPlotterValueTable: + def __init__(self, uid, parent, x, y, xsize, ysize, + headers=['Statistic Key ( Source Block :: Stat Name ) ', + 'Curent Value', 'Units', 'Description']): + # must encapsulate, cuz Qt's bases are not classes + self.uid = uid + self.treeWidget = QtGui.QTreeWidget(parent) + self.treeWidget.setColumnCount(len(headers)) + self.treeWidget.setGeometry(x,y,xsize,ysize) + self.treeWidget.setHeaderLabels(headers) + self.treeWidget.resizeColumnToContents(0) + + def updateItems(self, knobs, knobprops): + items = []; + self.treeWidget.clear() + for k, v in knobs.iteritems(): + items.append(QtGui.QTreeWidgetItem([str(k), str(v.value), + knobprops[k].units, + knobprops[k].description])) + self.treeWidget.insertTopLevelItems(0, items) diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/IceRadioClient.py b/gnuradio-core/src/python/gnuradio/ctrlport/IceRadioClient.py new file mode 100644 index 0000000000..0964b5a4ba --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/IceRadioClient.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +import Ice, Glacier2 +from PyQt4 import QtGui, QtCore +import sys, time, Ice +from gnuradio import gr +from gnuradio.ctrlport import GNURadio + +class IceRadioClient(Ice.Application): + def __init__(self, parentClass): + self.parentClass = parentClass + + def getRadio(self, host, port): + radiostr = "gnuradio -t:tcp -h " + host + " -p " + port + " -t 3000" + base = self.communicator().stringToProxy(radiostr).ice_twoway() + radio = GNURadio.ControlPortPrx.checkedCast(base) + + if not radio: + sys.stderr.write("{0} : invalid proxy.\n".format(args[0])) + return None + + return radio + + def run(self,args): + if len(args) < 2: + print "useage: [glacierinstance glacierhost glacierport] host port" + return + if len(args) == 8: + self.useglacier = True + guser = args[1] + gpass = args[2] + ginst = args[3] + ghost = args[4] + gport = args[5] + host = args[6] + port = args[7] + else: + self.useglacier = False + host = args[1] + port = args[2] + + if self.useglacier: + gstring = ginst + "/router -t:tcp -h " + ghost + " -p " + gport + print "GLACIER: {0}".format(gstring) + + setrouter = Glacier2.RouterPrx.checkedCast(self.communicator().stringToProxy(gstring)) + self.communicator().setDefaultRouter(setrouter) + defaultRouter = self.communicator().getDefaultRouter() + #defaultRouter = self.communicator().stringToProxy(gstring) + if not defaultRouter: + print self.appName() + ": no default router set" + return 1 + else: + print str(defaultRouter) + router = Glacier2.RouterPrx.checkedCast(defaultRouter) + if not router: + print self.appName() + ": configured router is not a Glacier2 router" + return 1 + + while True: + print "This demo accepts any user-id / password combination." + if not guser == '' and not gpass == '': + id = guser + pw = gpass + else: + id = raw_input("user id: ") + pw = raw_input("password: ") + + try: + router.createSession(id, pw) + break + except Glacier2.PermissionDeniedException, ex: + print "permission denied:\n" + ex.reason + + radio = self.getRadio(host, port) + if(radio is None): + return 1 + + app = QtGui.QApplication(sys.argv) + ex = self.parentClass(radio, port, self) + ex.show(); + app.exec_() diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/__init__.py b/gnuradio-core/src/python/gnuradio/ctrlport/__init__.py new file mode 100644 index 0000000000..031c3b424e --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/__init__.py @@ -0,0 +1,30 @@ +# +# Copyright 2012 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 this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# The presence of this file turns this directory into a Python package + +import Ice, IcePy + +# import swig generated symbols into the ctrlport namespace +#from ctrlport_swig import * +from monitor import * + +# import any pure python here +#import GNURadio diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/ctrlport-monitor b/gnuradio-core/src/python/gnuradio/ctrlport/ctrlport-monitor new file mode 100755 index 0000000000..0230c4cf7a --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/ctrlport-monitor @@ -0,0 +1,477 @@ +#!/usr/bin/env python + +from gnuradio import ctrlport + +from PyQt4.QtCore import Qt; +from PyQt4 import QtCore +import PyQt4.QtGui as QtGui +import sys, time, Ice, subprocess; +from gnuradio.ctrlport.IceRadioClient import *; +from gnuradio.ctrlport.DataPlotter import *; + +_gr_prefs = gr.prefs() +ice_directory = _gr_prefs.get_string('ctrlport', 'ice_directory', '') +print ice_directory +Ice.loadSlice(ice_directory + '/gnuradio.ice') + +import GNURadio + +class MAINWindow(QtGui.QMainWindow): + def minimumSizeHint(self): + return Qtgui.QSize(800,600); + def __init__(self, radio, port): + + super(MAINWindow, self).__init__() + self.plots = []; + + self.mdiArea = QtGui.QMdiArea() + self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.setCentralWidget(self.mdiArea) + + self.mdiArea.subWindowActivated.connect(self.updateMenus) + self.windowMapper = QtCore.QSignalMapper(self) + self.windowMapper.mapped[QtGui.QWidget].connect(self.setActiveSubWindow) + + self.createActions() + self.createMenus() + self.createToolBars() + self.createStatusBar() + self.updateMenus() + +# self.readSettings() + + self.setWindowTitle("GNU Radio Control Port Monitor") + self.setUnifiedTitleAndToolBarOnMac(True) + +# self.resize(QtCore.QSize(1024,768)); + + self.newCon(radio,port); +# self.mdiArea.addSubWindow(child); +# child.resize(QtCore.QSize(800,600)); + icon = QtGui.QIcon( ctrlport.__path__[0] + "/icon.png" ); + self.setWindowIcon(icon); + +# add empty plots ? +# self.plots = []; +# for i in range(3): +# self.newPlot(); + + def newCon(self,host=None,port=None): + child = MForm(host,port,self); + #child.setWindowTitle("Modem Connected :: %s:%s"%(host,port)); + child.setWindowTitle(str(host)); + self.mdiArea.addSubWindow(child); + child.resize(QtCore.QSize(800,600)); + child.showMaximized(); + return child; + + + def newUpd(self,k,r): + updater = UpdaterWindow(k,r,None); + updater.setWindowTitle("Updater: " + k); + self.mdiArea.addSubWindow(updater); + updater.show(); + + + def newSub(self,e): + tag = str(e.text(0)); + knobprop = self.knobprops[tag] + + if(type(knobprop.min) in [GNURadio.KnobVecB, GNURadio.KnobVecC, + GNURadio.KnobVecI,GNURadio.KnobVecF, + GNURadio.KnobVecD,GNURadio.KnobVecL]): + if(knobprop.display == ctrlport.DISPTIMESERIES): + #plot = self.newConstPlot(); + plot = self.newVecSeriesPlot(); + plot.setSeries(tag,tag); + plot.setWindowTitle(str(tag)); + else: # Plot others as XY for now + plot = self.newConstPlot(); + plot.setSeries(tag,tag); + plot.setWindowTitle(str(tag)); + elif(type(knobprop.min) in [GNURadio.KnobB,GNURadio.KnobC,GNURadio.KnobI, + GNURadio.KnobF,GNURadio.KnobD,GNURadio.KnobL]): + plot = self.newPlot(); + plot.addSeriesWithButton(tag, knobprop.description + ' (' + knobprop.units + ')', None, 1.0); + plot.setWindowTitle(str(tag)); + else: + print "WARNING: plotting of this knob-type not yet supported, ignoring attempt..." + + + def newVecSeriesPlot(self): + #plot = DataPlotterEqTaps(None, 'legend', 'title', 'xlabel', 'ylabel', 250, 0, 0, Qt.green) + plot = DataPlotterVectorOne(None, 'legend', 'title', 'xlabel', 'ylabel', 250, 0, 0); + self.mdiArea.addSubWindow(plot); + plot.dropSignal.connect(self.plotDropEvent ); + plot.show(); + self.plots.append(plot); + return plot; + + def newConstPlot(self): + plot = DataPlotterConst(None, 'legend', 'title', 'xlabel', 'ylabel', 250, 0, 0) + self.mdiArea.addSubWindow(plot); + plot.dropSignal.connect(self.plotDropEvent ); + plot.show(); + self.plots.append(plot); + return plot; + + def newPlot(self): + plot = DataPlotterTickerWithSeriesButtons(None,"",'units', '', 250,0,0,120); + self.mdiArea.addSubWindow(plot); + plot.dropSignal.connect(self.plotDropEvent ); + plot.show(); + self.plots.append(plot); + return plot; + + + def update(self, knobs): + for item in knobs.keys(): + for plot in self.plots: + plot.offerData( knobs[item].value, item ); + + def plotDropEvent(self, e): + model = QtGui.QStandardItemModel() + model.dropMimeData(e.mimeData(), QtCore.Qt.CopyAction,0,0,QtCore.QModelIndex()) + tag = str(QtGui.QTreeWidgetItem([model.item(0,0).text()]).text(0)); + knobprop = self.knobprops[tag] + try: + self.sender().addSeriesWithButton(tag, knobprop.description + ' (' + knobprop.units + ')', None, 1.0); + except: + print "This plot does not accept additional data items! ignoring..." + + + def setActiveSubWindow(self, window): + if window: + self.mdiArea.setActiveSubWindow(window) + + + def createActions(self): + self.newConAct = QtGui.QAction("&New Connection", + self, shortcut=QtGui.QKeySequence.New, + statusTip="Create a new file", triggered=self.newCon) + #self.newAct = QtGui.QAction(QtGui.QIcon(':/images/new.png'), "&New Plot", + self.newPlotAct = QtGui.QAction("&New Plot", + self, shortcut=QtGui.QKeySequence.New, + statusTip="Create a new file", triggered=self.newPlot) + + self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q", + statusTip="Exit the application", + triggered=QtGui.qApp.closeAllWindows) + + self.closeAct = QtGui.QAction("Cl&ose", self, shortcut="Ctrl+F4", + statusTip="Close the active window", + triggered=self.mdiArea.closeActiveSubWindow) + + self.closeAllAct = QtGui.QAction("Close &All", self, + statusTip="Close all the windows", + triggered=self.mdiArea.closeAllSubWindows) + + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_T); + self.tileAct = QtGui.QAction("&Tile", self, + statusTip="Tile the windows", + triggered=self.mdiArea.tileSubWindows, + shortcut=qks) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_C); + self.cascadeAct = QtGui.QAction("&Cascade", self, + statusTip="Cascade the windows", shortcut=qks, + triggered=self.mdiArea.cascadeSubWindows) + + self.nextAct = QtGui.QAction("Ne&xt", self, + shortcut=QtGui.QKeySequence.NextChild, + statusTip="Move the focus to the next window", + triggered=self.mdiArea.activateNextSubWindow) + + self.previousAct = QtGui.QAction("Pre&vious", self, + shortcut=QtGui.QKeySequence.PreviousChild, + statusTip="Move the focus to the previous window", + triggered=self.mdiArea.activatePreviousSubWindow) + + self.separatorAct = QtGui.QAction(self) + self.separatorAct.setSeparator(True) + + self.aboutAct = QtGui.QAction("&About", self, + statusTip="Show the application's About box", + triggered=self.about) + + self.aboutQtAct = QtGui.QAction("About &Qt", self, + statusTip="Show the Qt library's About box", + triggered=QtGui.qApp.aboutQt) + + def createMenus(self): + self.fileMenu = self.menuBar().addMenu("&File") + self.fileMenu.addAction(self.newConAct) + self.fileMenu.addAction(self.newPlotAct) + self.fileMenu.addSeparator() + self.fileMenu.addAction(self.exitAct) + + self.windowMenu = self.menuBar().addMenu("&Window") + self.updateWindowMenu() + self.windowMenu.aboutToShow.connect(self.updateWindowMenu) + + self.menuBar().addSeparator() + + self.helpMenu = self.menuBar().addMenu("&Help") + self.helpMenu.addAction(self.aboutAct) + self.helpMenu.addAction(self.aboutQtAct) + + + def createToolBars(self): + self.fileToolBar = self.addToolBar("File") + self.fileToolBar.addAction(self.newConAct) + self.fileToolBar.addAction(self.newPlotAct) + + self.fileToolBar = self.addToolBar("Window") + self.fileToolBar.addAction(self.tileAct) + self.fileToolBar.addAction(self.cascadeAct) + + def createStatusBar(self): + self.statusBar().showMessage("Ready") + + + def activeMdiChild(self): + activeSubWindow = self.mdiArea.activeSubWindow() + if activeSubWindow: + return activeSubWindow.widget() + return None + + def updateMenus(self): + hasMdiChild = (self.activeMdiChild() is not None) + self.closeAct.setEnabled(hasMdiChild) + self.closeAllAct.setEnabled(hasMdiChild) + self.tileAct.setEnabled(hasMdiChild) + self.cascadeAct.setEnabled(hasMdiChild) + self.nextAct.setEnabled(hasMdiChild) + self.previousAct.setEnabled(hasMdiChild) + self.separatorAct.setVisible(hasMdiChild) + + + def updateWindowMenu(self): + self.windowMenu.clear() + self.windowMenu.addAction(self.closeAct) + self.windowMenu.addAction(self.closeAllAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.tileAct) + self.windowMenu.addAction(self.cascadeAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.nextAct) + self.windowMenu.addAction(self.previousAct) + self.windowMenu.addAction(self.separatorAct) + + def about(self): + about_info = \ +'''Copyright 2012 Free Software Foundation, Inc.\n +This program is part of GNU Radio.\n +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.\n +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.\n +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.''' + + QtGui.QMessageBox.about(None, "gr-ctrlport-monitor", about_info) + +class ConInfoDialog(QtGui.QDialog): + def __init__(self, parent=None): + super(ConInfoDialog, self).__init__(parent) + + self.gridLayout = QtGui.QGridLayout(self) + + + self.host = QtGui.QLineEdit(self); + self.port = QtGui.QLineEdit(self); + self.host.setText("localhost"); + self.port.setText("43243"); + + self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) + + self.gridLayout.addWidget(self.host); + self.gridLayout.addWidget(self.port); + self.gridLayout.addWidget(self.buttonBox); + + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + + def accept(self): + self.done(1); + + def reject(self): + self.done(0); + +class UpdaterWindow(QtGui.QWidget): + def __init__(self, key, radio, parent): + QtGui.QWidget.__init__(self,parent) + + self.key = key; + self.radio = radio; + + self.resize(300,200) + self.layout = QtGui.QVBoxLayout(); + + self.textInput = QtGui.QLineEdit(); + self.setButton = QtGui.QPushButton("Set Value") + + rv = radio.get([key]); + self.textInput.setText(str(rv[key].value)); + self.sv = rv[key]; + + + self.props = radio.properties([key])[key]; + info = str(self.props); + + self.infoLabel = QtGui.QLabel(info); + self.layout.addWidget(self.infoLabel); + + self.layout.addWidget(self.textInput); + + self.setButton.connect(self.setButton, QtCore.SIGNAL('clicked()'), self._set) + + self.is_num = ((type(self.sv.value)==float) or (type(self.sv.value)==int)); + if(self.is_num): + self.sliderlayout = QtGui.QHBoxLayout(); + + self.slider = QtGui.QSlider(Qt.Horizontal); + + self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.min.value))); + self.sliderlayout.addWidget(self.slider); + self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.max.value))); + + + self.steps = 10000; + self.valspan = self.props.max.value - self.props.min.value; + + #self.slider.setRange( self.props.min.value, self.props.max.value ); + self.slider.setRange( 0,10000 ); + + self.slider.setValue( self.sv.value ); + self.slider.setValue( self.steps*(self.sv.value-self.props.min.value)/self.valspan); + + self.connect(self.slider, QtCore.SIGNAL("sliderReleased()"), self._slide); + + self.layout.addLayout(self.sliderlayout); + + self.layout.addWidget(self.setButton); + + # set layout and go... + self.setLayout(self.layout); + + def _slide(self): + val = (self.slider.value()*self.valspan + self.props.min.value)/float(self.steps); + self.textInput.setText(str(val)); + + def _set(self): + if(type(self.sv.value) == str): + val = str(self.textInput.text()); + elif(type(self.sv.value) == int): + val = int(round(float(self.textInput.text()))); + elif(type(self.sv.value) == float): + val = float(self.textInput.text()); + else: + print "set type not supported! (%s)"%(type(self.sv.value)); + sys.exit(-1); + #self.sv.value = int(val); + self.sv.value = val; + km = {}; + km[self.key] = self.sv; + self.radio.set(km); + +class MForm(QtGui.QWidget): + def update(self): + try: + st = time.time(); + knobs = self.radio.get([]); + ft = time.time(); + latency = ft-st; + self.parent.statusBar().showMessage("Current GNU Radio Control Port Query Latency: %f ms"%(latency*1000)) + + except Exception, e: + sys.stderr.write("ctrlport-monitor: radio.get threw exception ({0}).\n".format(e)) + if(type(self.parent) is MAINWindow): + # closing all of these seems to help control shutdown order. + self.parent.closeAct.trigger() + else: + sys.exit(1) + return + + tableitems = knobs.keys(); + + #UPDATE TABLE: + self.table.updateItems(knobs, self.knobprops) + + #UPDATE PLOTS + self.parent.update( knobs ); + + + def __init__(self, radio=None, port=None, parent=None): + + super(MForm, self).__init__() + + if(radio == None or port == None): + askinfo = ConInfoDialog(self); + if askinfo.exec_(): + print "connecting..." + radio = str(askinfo.host.text()); + port = str(askinfo.port.text()); + print "this is broken" + return; + else: + return; + + self.parent = parent; + self.horizontalLayout = QtGui.QVBoxLayout(self); + self.gridLayout = QtGui.QGridLayout() + + + self.radio = radio + self.knobprops = self.radio.properties([]) + self.parent.knobprops = self.knobprops; + self.resize(775,500) + self.timer = QtCore.QTimer() + self.constupdatediv = 0 + self.tableupdatediv = 0 + plotsize=250; + + + # make table + self.table = DataPlotterValueTable(self, 0, 0, 400, 200); + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + self.table.treeWidget.setSizePolicy(sizePolicy); + self.table.treeWidget.setEditTriggers(QtGui.QAbstractItemView.EditKeyPressed); + self.table.treeWidget.setSortingEnabled(True); + self.table.treeWidget.setDragEnabled(True) + + # add things to layouts + self.horizontalLayout.addWidget(self.table.treeWidget); + + # set up timer + self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update); + self.connect(self.table.treeWidget, QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.parent.newSub); + + # set up context menu .. + self.table.treeWidget.setContextMenuPolicy(Qt.CustomContextMenu); + self.table.treeWidget.customContextMenuRequested.connect(self.openMenu); + + self.timer.start(2000) + + def plotDropEvent(self, e): + model = QtGui.QStandardItemModel() + model.dropMimeData(e.mimeData(), QtCore.Qt.CopyAction,0,0,QtCore.QModelIndex()) + tag = str(QtGui.QTreeWidgetItem([model.item(0,0).text()]).text(0)); + knobprop = self.knobprops[tag] + self.sender().addSeriesWithButton(tag, knobprop.description + ' (' + knobprop.units + ')', None, 1.0); + + def openMenu(self, pos): + index = self.table.treeWidget.selectedIndexes(); + item = self.table.treeWidget.itemFromIndex(index[0]); + itemname = str(item.text(0)); + self.parent.newUpd(itemname, self.radio); +# updater = UpdaterWindow(itemname, self.radio, self.parent); +# updater.setWindowTitle("Updater: " + itemname); +# self.parent.mdiArea.addSubWindow(updater); +# print "done" + + +class MyClient(IceRadioClient): + def __init__(self): IceRadioClient.__init__(self, MAINWindow) + +sys.exit(MyClient().main(sys.argv)) diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/gr-ctrlport-monitor b/gnuradio-core/src/python/gnuradio/ctrlport/gr-ctrlport-monitor new file mode 100755 index 0000000000..241b8a2043 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/gr-ctrlport-monitor @@ -0,0 +1,581 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +from gnuradio import gr, ctrlport + +from PyQt4 import QtCore,Qt +import PyQt4.QtGui as QtGui +import sys, time + +import Ice +from gnuradio.ctrlport.IceRadioClient import * +from gnuradio.ctrlport.GrDataPlotter import * +from gnuradio.ctrlport import GNURadio + +class MAINWindow(QtGui.QMainWindow): + def minimumSizeHint(self): + return Qtgui.QSize(800,600) + + def __init__(self, radio, port, interface): + + super(MAINWindow, self).__init__() + self.conns = [] + self.plots = [] + self.knobprops = [] + self.interface = interface + + self.mdiArea = QtGui.QMdiArea() + self.mdiArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.mdiArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) + self.setCentralWidget(self.mdiArea) + + self.mdiArea.subWindowActivated.connect(self.updateMenus) + self.windowMapper = QtCore.QSignalMapper(self) + self.windowMapper.mapped[QtGui.QWidget].connect(self.setActiveSubWindow) + + self.createActions() + self.createMenus() + self.createToolBars() + self.createStatusBar() + self.updateMenus() + + self.setWindowTitle("GNU Radio Control Port Monitor") + self.setUnifiedTitleAndToolBarOnMac(True) + + self.newCon(radio, port) + icon = QtGui.QIcon(ctrlport.__path__[0] + "/icon.png" ) + self.setWindowIcon(icon) + + def newCon(self, radio=None, port=None): + child = MForm(radio, port, len(self.conns), self) + if(child.radio is not None): + child.setWindowTitle(str(child.radio)) + self.mdiArea.addSubWindow(child) + child.showMaximized() + self.conns.append(child) + self.plots.append([]) + + def propertiesMenu(self, key, radio, uid): + r = str(radio).split(" ") + title = "{0}:{1}".format(r[3], r[5]) + + props = radio.properties([key]) + pmin = props[key].min.value + pmax = props[key].max.value + if pmin == []: + pmin = None + else: + pmin = 1.1*pmin + if pmax == []: + pmax = None + else: + pmax = 1.1*pmax + + def newUpdaterProxy(): + self.newUpdater(key, radio) + + def newPlotterFProxy(): + self.newPlotF(key, uid, title, pmin, pmax) + + def newPlotterCProxy(): + self.newPlotC(key, uid, title, pmin, pmax) + + def newPlotterConstProxy(): + self.newPlotConst(key, uid, title, pmin, pmax) + + def newPlotterPsdFProxy(): + self.newPlotPsdF(key, uid, title) + + def newPlotterPsdCProxy(): + self.newPlotPsdC(key, uid, title) + + menu = QtGui.QMenu(self) + menu.setTitle("Item Actions") + menu.setTearOffEnabled(False) + + # object properties + menu.addAction("Properties", newUpdaterProxy) + + # displays available if not complex + menu.addAction("Plot Time", newPlotterFProxy) + menu.addAction("Plot PSD", newPlotterPsdFProxy) + + # displays available if complex + menu.addAction("Plot Time (cpx)", newPlotterCProxy) + menu.addAction("Plot Constellation", newPlotterConstProxy) + menu.addAction("Plot PSD cpx", newPlotterPsdCProxy) + + menu.popup(QtGui.QCursor.pos()) + + def newUpdater(self, key, radio): + updater = UpdaterWindow(key, radio, None) + updater.setWindowTitle("Updater: " + key) + updater.setModal(False) + updater.exec_() + + def newSub(self, e): + tag = str(e.text(0)) + tree = e.treeWidget().parent() + uid = tree.uid + knobprop = self.knobprops[uid][tag] + + r = str(tree.radio).split(" ") + title = "{0}:{1}".format(r[3], r[5]) + pmin = knobprop.min.value + pmax = knobprop.max.value + if pmin == []: + pmin = None + else: + pmin = 1.1*pmin + if pmax == []: + pmax = None + else: + pmax = 1.1*pmax + + if(knobprop.display == GNURadio.DisplayType.DISPXYSCATTER): + self.newPlotConst(tag, uid, title, pmin, pmax) + elif(knobprop.display == GNURadio.DisplayType.DISPTIMESERIESF): + self.newPlotF(tag, uid, title, pmin, pmax) + elif(knobprop.display == GNURadio.DisplayType.DISPTIMESERIESC): + self.newPlotC(tag, uid, title, pmin, pmax) + + def createPlot(self, plot, uid, title): + plot.start() + self.plots[uid].append(plot) + + self.mdiArea.addSubWindow(plot.qwidget()) + plot.qwidget().setWindowTitle("{0}: {1}".format(title, plot.name())) + self.connect(plot.qwidget(), + QtCore.SIGNAL('destroyed(QObject*)'), + self.destroyPlot) + plot.qwidget().show() + + + def destroyPlot(self, obj): + for plots in self.plots: + for p in plots: + if p.qwidget() == obj: + plots.remove(p) + break + + def newPlotConst(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrDataPlotterConst(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def newPlotF(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrDataPlotterF(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def newPlotC(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrDataPlotterC(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def newPlotPsdF(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrDataPlotterPsdF(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def newPlotPsdC(self, tag, uid, title="", pmin=None, pmax=None): + plot = GrDataPlotterPsdC(tag, 32e6, pmin, pmax) + self.createPlot(plot, uid, title) + + def update(self, knobs, uid): + #sys.stderr.write("KNOB KEYS: {0}\n".format(knobs.keys())) + for plot in self.plots[uid]: + data = knobs[plot.name()].value + plot.update(data) + plot.stop() + plot.wait() + plot.start() + + def setActiveSubWindow(self, window): + if window: + self.mdiArea.setActiveSubWindow(window) + + + def createActions(self): + self.newConAct = QtGui.QAction("&New Connection", + self, shortcut=QtGui.QKeySequence.New, + statusTip="Create a new file", triggered=self.newCon) + #self.newAct = QtGui.QAction(QtGui.QIcon(':/images/new.png'), "&New Plot", + self.newPlotAct = QtGui.QAction("&New Plot", + self, + statusTip="Create a new file", triggered=self.newPlotF) + + self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q", + statusTip="Exit the application", + triggered=QtGui.qApp.closeAllWindows) + + self.closeAct = QtGui.QAction("Cl&ose", self, shortcut="Ctrl+F4", + statusTip="Close the active window", + triggered=self.mdiArea.closeActiveSubWindow) + + self.closeAllAct = QtGui.QAction("Close &All", self, + statusTip="Close all the windows", + triggered=self.mdiArea.closeAllSubWindows) + + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_T); + self.tileAct = QtGui.QAction("&Tile", self, + statusTip="Tile the windows", + triggered=self.mdiArea.tileSubWindows, + shortcut=qks) + + qks = QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_C); + self.cascadeAct = QtGui.QAction("&Cascade", self, + statusTip="Cascade the windows", shortcut=qks, + triggered=self.mdiArea.cascadeSubWindows) + + self.nextAct = QtGui.QAction("Ne&xt", self, + shortcut=QtGui.QKeySequence.NextChild, + statusTip="Move the focus to the next window", + triggered=self.mdiArea.activateNextSubWindow) + + self.previousAct = QtGui.QAction("Pre&vious", self, + shortcut=QtGui.QKeySequence.PreviousChild, + statusTip="Move the focus to the previous window", + triggered=self.mdiArea.activatePreviousSubWindow) + + self.separatorAct = QtGui.QAction(self) + self.separatorAct.setSeparator(True) + + self.aboutAct = QtGui.QAction("&About", self, + statusTip="Show the application's About box", + triggered=self.about) + + self.aboutQtAct = QtGui.QAction("About &Qt", self, + statusTip="Show the Qt library's About box", + triggered=QtGui.qApp.aboutQt) + + def createMenus(self): + self.fileMenu = self.menuBar().addMenu("&File") + self.fileMenu.addAction(self.newConAct) + self.fileMenu.addAction(self.newPlotAct) + self.fileMenu.addSeparator() + self.fileMenu.addAction(self.exitAct) + + self.windowMenu = self.menuBar().addMenu("&Window") + self.updateWindowMenu() + self.windowMenu.aboutToShow.connect(self.updateWindowMenu) + + self.menuBar().addSeparator() + + self.helpMenu = self.menuBar().addMenu("&Help") + self.helpMenu.addAction(self.aboutAct) + self.helpMenu.addAction(self.aboutQtAct) + + def createToolBars(self): + self.fileToolBar = self.addToolBar("File") + self.fileToolBar.addAction(self.newConAct) + self.fileToolBar.addAction(self.newPlotAct) + + self.fileToolBar = self.addToolBar("Window") + self.fileToolBar.addAction(self.tileAct) + self.fileToolBar.addAction(self.cascadeAct) + + def createStatusBar(self): + self.statusBar().showMessage("Ready") + + + def activeMdiChild(self): + activeSubWindow = self.mdiArea.activeSubWindow() + if activeSubWindow: + return activeSubWindow.widget() + return None + + def updateMenus(self): + hasMdiChild = (self.activeMdiChild() is not None) + self.closeAct.setEnabled(hasMdiChild) + self.closeAllAct.setEnabled(hasMdiChild) + self.tileAct.setEnabled(hasMdiChild) + self.cascadeAct.setEnabled(hasMdiChild) + self.nextAct.setEnabled(hasMdiChild) + self.previousAct.setEnabled(hasMdiChild) + self.separatorAct.setVisible(hasMdiChild) + + def updateWindowMenu(self): + self.windowMenu.clear() + self.windowMenu.addAction(self.closeAct) + self.windowMenu.addAction(self.closeAllAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.tileAct) + self.windowMenu.addAction(self.cascadeAct) + self.windowMenu.addSeparator() + self.windowMenu.addAction(self.nextAct) + self.windowMenu.addAction(self.previousAct) + self.windowMenu.addAction(self.separatorAct) + + def about(self): + about_info = \ +'''Copyright 2012 Free Software Foundation, Inc.\n +This program is part of GNU Radio.\n +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.\n +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.\n +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.''' + + QtGui.QMessageBox.about(None, "gr-ctrlport-monitor", about_info) + + +class ConInfoDialog(QtGui.QDialog): + def __init__(self, parent=None): + super(ConInfoDialog, self).__init__(parent) + + self.gridLayout = QtGui.QGridLayout(self) + + + self.host = QtGui.QLineEdit(self); + self.port = QtGui.QLineEdit(self); + self.host.setText("localhost"); + self.port.setText("43243"); + + self.buttonBox = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) + + self.gridLayout.addWidget(self.host); + self.gridLayout.addWidget(self.port); + self.gridLayout.addWidget(self.buttonBox); + + self.buttonBox.accepted.connect(self.accept) + self.buttonBox.rejected.connect(self.reject) + + + def accept(self): + self.done(1); + + def reject(self): + self.done(0); + + +class UpdaterWindow(QtGui.QDialog): + def __init__(self, key, radio, parent): + QtGui.QDialog.__init__(self, parent) + + self.key = key; + self.radio = radio + + self.resize(300,200) + self.layout = QtGui.QVBoxLayout() + + self.props = radio.properties([key])[key] + info = str(self.props) + + self.infoLabel = QtGui.QLabel(info) + self.layout.addWidget(self.infoLabel) + + # Test here to make sure that a 'set' function + try: + a = radio.set(radio.get([key])) + has_set = True + except Ice.UnknownException: + has_set = False + + if(has_set is False): + self.cancelButton = QtGui.QPushButton("Ok") + self.cancelButton.connect(self.cancelButton, QtCore.SIGNAL('clicked()'), self.reject) + + self.buttonlayout = QtGui.QHBoxLayout() + self.buttonlayout.addWidget(self.cancelButton) + self.layout.addLayout(self.buttonlayout) + + else: # we have a set function + self.textInput = QtGui.QLineEdit() + self.layout.addWidget(self.textInput) + + self.applyButton = QtGui.QPushButton("Apply") + self.setButton = QtGui.QPushButton("OK") + self.cancelButton = QtGui.QPushButton("Cancel") + + rv = radio.get([key]) + self.textInput.setText(str(rv[key].value)) + self.sv = rv[key] + + self.applyButton.connect(self.applyButton, QtCore.SIGNAL('clicked()'), self._apply) + self.setButton.connect(self.setButton, QtCore.SIGNAL('clicked()'), self._set) + self.cancelButton.connect(self.cancelButton, QtCore.SIGNAL('clicked()'), self.reject) + + self.is_num = ((type(self.sv.value)==float) or (type(self.sv.value)==int)) + if(self.is_num): + self.sliderlayout = QtGui.QHBoxLayout() + + self.slider = QtGui.QSlider(QtCore.Qt.Horizontal) + + self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.min.value))) + self.sliderlayout.addWidget(self.slider) + self.sliderlayout.addWidget(QtGui.QLabel(str(self.props.max.value))) + + self.steps = 10000 + self.valspan = self.props.max.value - self.props.min.value + + self.slider.setRange(0, 10000) + self._set_slider_value(self.sv.value) + + self.connect(self.slider, QtCore.SIGNAL("sliderReleased()"), self._slide) + + self.layout.addLayout(self.sliderlayout) + + self.buttonlayout = QtGui.QHBoxLayout() + self.buttonlayout.addWidget(self.applyButton) + self.buttonlayout.addWidget(self.setButton) + self.buttonlayout.addWidget(self.cancelButton) + self.layout.addLayout(self.buttonlayout) + + # set layout and go... + self.setLayout(self.layout) + + def _set_slider_value(self, val): + self.slider.setValue(self.steps*(val-self.props.min.value)/self.valspan) + + def _slide(self): + val = (self.slider.value()*self.valspan + self.props.min.value)/float(self.steps) + self.textInput.setText(str(val)) + + def _apply(self): + if(type(self.sv.value) == str): + val = str(self.textInput.text()) + elif(type(self.sv.value) == int): + val = int(round(float(self.textInput.text()))) + elif(type(self.sv.value) == float): + val = float(self.textInput.text()) + else: + sys.stderr.write("set type not supported! ({0})\n".format(type(self.sv.value))) + sys.exit(-1) + + self.sv.value = val + km = {} + km[self.key] = self.sv + self.radio.set(km) + self._set_slider_value(self.sv.value) + + def _set(self): + self._apply() + self.done(0) + + +class MForm(QtGui.QWidget): + def update(self): + try: + st = time.time(); + knobs = self.radio.get([]); + ft = time.time(); + latency = ft-st; + self.parent.statusBar().showMessage("Current GNU Radio Control Port Query Latency: %f ms"%(latency*1000)) + + except Exception, e: + sys.stderr.write("ctrlport-monitor: radio.get threw exception ({0}).\n".format(e)) + if(type(self.parent) is MAINWindow): + # Find window of connection + remove = [] + for p in self.parent.mdiArea.subWindowList(): + if self.parent.conns[self.uid] == p.widget(): + remove.append(p) + + # Find any subplot windows of connection + for p in self.parent.mdiArea.subWindowList(): + for plot in self.parent.plots[self.uid]: + if plot.qwidget() == p.widget(): + remove.append(p) + + # Clean up local references to these + self.parent.conns.remove(self.parent.conns[self.uid]) + self.parent.plots.remove(self.parent.plots[self.uid]) + + # Remove subwindows for connection and plots + for r in remove: + self.parent.mdiArea.removeSubWindow(r) + + # Clean up self + self.close() + else: + sys.exit(1) + return + + tableitems = knobs.keys() + + #UPDATE TABLE: + self.table.updateItems(knobs, self.knobprops) + + #UPDATE PLOTS + self.parent.update(knobs, self.uid) + + + def __init__(self, radio=None, port=None, uid=0, parent=None): + + super(MForm, self).__init__() + + if(radio == None or port == None): + askinfo = ConInfoDialog(self); + if askinfo.exec_(): + host = str(askinfo.host.text()); + port = str(askinfo.port.text()); + radio = parent.interface.getRadio(host, port) + else: + self.radio = None + return + + self.uid = uid + self.parent = parent + self.horizontalLayout = QtGui.QVBoxLayout(self) + self.gridLayout = QtGui.QGridLayout() + + self.radio = radio + self.knobprops = self.radio.properties([]) + self.parent.knobprops.append(self.knobprops) + self.resize(775,500) + self.timer = QtCore.QTimer() + self.constupdatediv = 0 + self.tableupdatediv = 0 + plotsize=250 + + # make table + self.table = GrDataPlotterValueTable(uid, self, 0, 0, 400, 200) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) + self.table.treeWidget.setSizePolicy(sizePolicy) + self.table.treeWidget.setEditTriggers(QtGui.QAbstractItemView.EditKeyPressed) + self.table.treeWidget.setSortingEnabled(True) + self.table.treeWidget.setDragEnabled(True) + + # add things to layouts + self.horizontalLayout.addWidget(self.table.treeWidget) + + # set up timer + self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.update) + self.timer.start(1000) + + # set up context menu .. + self.table.treeWidget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.table.treeWidget.customContextMenuRequested.connect(self.openMenu) + + # Set up double-click to launch default plotter + self.connect(self.table.treeWidget, + QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), + self.parent.newSub); + + def openMenu(self, pos): + index = self.table.treeWidget.selectedIndexes() + item = self.table.treeWidget.itemFromIndex(index[0]) + itemname = str(item.text(0)) + self.parent.propertiesMenu(itemname, self.radio, self.uid) + + +class MyClient(IceRadioClient): + def __init__(self): + IceRadioClient.__init__(self, MAINWindow) + +sys.exit(MyClient().main(sys.argv)) diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/icon.png b/gnuradio-core/src/python/gnuradio/ctrlport/icon.png Binary files differnew file mode 100644 index 0000000000..4beb204428 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/icon.png diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/monitor.py b/gnuradio-core/src/python/gnuradio/ctrlport/monitor.py new file mode 100644 index 0000000000..1e74a814f0 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/monitor.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +import sys, subprocess, re, signal, time, atexit, os +from gnuradio import gr + +class monitor: + def __init__(self): + print "ControlPort Monitor running." + self.started = False + self.start() + atexit.register(self.shutdown) + + def __del__(self): + if(self.started): + self.stop() + + def start(self): + print "monitor::endpoints() = %s" % (gr.rpcmanager_get().endpoints()) + try: + self.proc = subprocess.Popen(map(lambda a: ["gr-ctrlport-monitor", + re.search("\d+\.\d+\.\d+\.\d+",a).group(0), + re.search("-p (\d+)",a).group(1)], + gr.rpcmanager_get().endpoints())[0]) + self.started = True + except: + self.proc = None + print "failed to to start ControlPort Monitor on specified port" + + def stop(self): + if(self.proc): + if(self.proc.returncode == None): + print "\tcalling stop on shutdown" + self.proc.terminate() + else: + print "\tno proc to shut down, exiting" + + def shutdown(self): + print "ctrlport.monitor received shutdown signal" + if(self.started): + self.stop() diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/qa_cpp_py_binding.py b/gnuradio-core/src/python/gnuradio/ctrlport/qa_cpp_py_binding.py new file mode 100755 index 0000000000..760252b4df --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/qa_cpp_py_binding.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +# +# This program tests mixed python and c++ ctrlport exports in a single app +# + +import Ice +import sys, time, random, numpy +from gnuradio import gr, gr_unittest + +from gnuradio.ctrlport import GNURadio +from gnuradio import ctrlport + +def get1(): + return "success" + +def get2(): + return "failure" + +class inc_class: + def __init__(self): + self.val = 1 + def pp(self): + self.val = self.val+1 + return self.val + +get3 = inc_class() + +def get4(): + random.seed(0) + rv = random.random() + return rv + +def get5(): + numpy.random.seed(0) + samp_t = numpy.random.randn(24)+1j*numpy.random.randn(24); + samp_f = numpy.fft.fft(samp_t); + log_pow_f = 20*numpy.log10(numpy.abs(samp_f)) + rv = list(log_pow_f) + return rv; + +def get6(): + numpy.random.seed(0) + samp_t = numpy.random.randn(1024)+1j*numpy.random.randn(1024); + rv = list(samp_t) + return rv; + +class test_cpp_py_binding(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001(self): + v1 = gr.RPC_get_string("pyland", "v1", "unit_1_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + v1.activate(get1) + + v2 = gr.RPC_get_string("pyland", "v2", "unit_2_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + v2.activate(get2) + + v3 = gr.RPC_get_int("pyland", "v3", "unit_3_int", + "Python Exported Int", 0, 100, 1, + gr.DISPNULL) + v3.activate(get3.pp) + + v4 = gr.RPC_get_double("pyland", "time", "unit_4_time_double", + "Python Exported Double", 0, 1000, 1, + gr.DISPNULL) + v4.activate(get4) + + v5 = gr.RPC_get_vector_float("pyland", "fvec", "unit_5_float_vector", + "Python Exported Float Vector", [], [], [], + gr.DISPTIMESERIESC) + v5.activate(get5) + + v6 = gr.RPC_get_vector_gr_complex("pyland", "cvec", "unit_6_gr_complex_vector", + "Python Exported Complex Vector", [], [], [], + gr.DISPXYSCATTER) + v6.activate(get6) + + # print some variables locally + val = get1() + rval = v1.get() + self.assertEqual(val, rval) + + val = get2() + rval = v2.get() + self.assertEqual(val, rval) + + val = get3.pp() + rval = v3.get() + self.assertEqual(val+1, rval) + + val = get4() + rval = v4.get() + self.assertEqual(val, rval) + + val = get5() + rval = v5.get() + self.assertComplexTuplesAlmostEqual(val, rval, 5) + + val = get6() + rval = v6.get() + self.assertComplexTuplesAlmostEqual(val, rval, 5) + + def test_002(self): + data = range(1,9) + + self.src = gr.vector_source_c(data) + self.p1 = gr.ctrlport_probe_c("aaa","C++ exported variable") + self.p2 = gr.ctrlport_probe_c("bbb","C++ exported variable") + probe_name = self.p2.name() + str(self.p2.unique_id()) + + self.tb.connect(self.src, self.p1) + self.tb.connect(self.src, self.p2) + self.tb.start() + + # Probes return complex values as list of floats with re, im + # Imaginary parts of this data set are 0. + expected_result = [1, 0, 2, 0, 3, 0, 4, 0, + 5, 0, 6, 0, 7, 0, 8, 0] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + + # Initialize a simple Ice client from endpoint + ic = Ice.initialize(sys.argv) + base = ic.stringToProxy(ep) + radio = GNURadio.ControlPortPrx.checkedCast(base) + + # Get all exported knobs + ret = radio.get([probe_name + "::bbb"]) + for name in ret.keys(): + result = ret[name].value + self.assertEqual(result, expected_result) + + self.tb.stop() + +if __name__ == '__main__': + gr_unittest.run(test_cpp_py_binding, "test_cpp_py_binding.xml") + diff --git a/gnuradio-core/src/python/gnuradio/ctrlport/qa_cpp_py_binding_set.py b/gnuradio-core/src/python/gnuradio/ctrlport/qa_cpp_py_binding_set.py new file mode 100755 index 0000000000..fe7e5bcb60 --- /dev/null +++ b/gnuradio-core/src/python/gnuradio/ctrlport/qa_cpp_py_binding_set.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python +# +# Copyright 2012 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. +# + +# +# This program tests mixed python and c++ GRCP sets in a single app +# + +import Ice +import sys, time, random, numpy +from gnuradio import gr, gr_unittest + +from gnuradio.ctrlport import GNURadio +from gnuradio import ctrlport + +class inc_class: + def __init__(self,val): + self.val = val; + + def _get(self): + #print "returning get (val = %s)"%(str(self.val)); + return self.val; + + def _set(self,val): + #print "updating val to %s"%(str(val)); + self.val = val; + return; + +getset1 = inc_class(10); +getset2 = inc_class(100.0); +getset3 = inc_class("test"); + +class test_cpp_py_binding_set(gr_unittest.TestCase): + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001(self): + + g1 = gr.RPC_get_int("pyland", "v1", "unit_1_int", + "Python Exported Int", 0, 100, 10, + gr.DISPNULL) + g1.activate(getset1._get) + s1 = gr.RPC_get_int("pyland", "v1", "unit_1_int", + "Python Exported Int", 0, 100, 10, + gr.DISPNULL) + s1.activate(getset1._set) + time.sleep(0.01) + + # test int variables + getset1._set(21) + val = getset1._get() + rval = g1.get() + self.assertEqual(val, rval) + + g2 = gr.RPC_get_float("pyland", "v2", "unit_2_float", + "Python Exported Float", -100, 1000.0, 100.0, + gr.DISPNULL) + g2.activate(getset2._get) + s2 = gr.RPC_get_float("pyland", "v2", "unit_2_float", + "Python Exported Float", -100, 1000.0, 100.0, + gr.DISPNULL) + s2.activate(getset2._set) + time.sleep(0.01) + + # test float variables + getset2._set(123.456) + val = getset2._get() + rval = g2.get() + self.assertAlmostEqual(val, rval, 4) + + g3 = gr.RPC_get_string("pyland", "v3", "unit_3_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + g3.activate(getset3._get) + s3 = gr.RPC_get_string("pyland", "v3", "unit_3_string", + "Python Exported String", "", "", "", + gr.DISPNULL) + s3.activate(getset3._set) + time.sleep(0.01) + + # test string variables + getset3._set("third test") + val = getset3._get() + rval = g3.get() + self.assertEqual(val, rval) + + + def test_002(self): + data = range(1, 10) + + self.src = gr.vector_source_c(data, True) + self.p = gr.nop(gr.sizeof_gr_complex) + self.p.set_ctrlport_test(0); + probe_info = "{0}{1}".format(self.p.name(), self.p.unique_id()) + + self.tb.connect(self.src, self.p) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + + # Initialize a simple Ice client from endpoint + ic = Ice.initialize(sys.argv) + base = ic.stringToProxy(ep) + radio = GNURadio.ControlPortPrx.checkedCast(base) + + self.tb.start() + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get all exported knobs + key_name_test = probe_info+"::test" + ret = radio.get([key_name_test,]) + + ret[key_name_test].value = 10 + radio.set({key_name_test: ret[key_name_test]}) + + ret = radio.get([]) + result_test = ret[key_name_test].value + self.assertEqual(result_test, 10) + + self.tb.stop() + self.tb.wait() + +if __name__ == '__main__': + gr_unittest.run(test_cpp_py_binding_set, "test_cpp_py_binding_set.xml") + diff --git a/gnuradio-core/src/python/gnuradio/gr/CMakeLists.txt b/gnuradio-core/src/python/gnuradio/gr/CMakeLists.txt index 62f3d7e46d..c5ff2a0c14 100644 --- a/gnuradio-core/src/python/gnuradio/gr/CMakeLists.txt +++ b/gnuradio-core/src/python/gnuradio/gr/CMakeLists.txt @@ -47,6 +47,7 @@ foreach(py_qa_test_file ${py_qa_test_files}) ${CMAKE_SOURCE_DIR}/gruel/src/python ${CMAKE_BINARY_DIR}/gruel/src/swig ${CMAKE_BINARY_DIR}/gnuradio-core/src/python + ${CMAKE_BINARY_DIR}/gnuradio-core/src/python/ctrlport ${CMAKE_BINARY_DIR}/gnuradio-core/src/lib/swig ) set(GR_TEST_TARGET_DEPS volk gruel gnuradio-core) diff --git a/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py b/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py index a87ed87eef..239911851c 100755 --- a/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py +++ b/gnuradio-core/src/python/gnuradio/gr/qa_nlog10.py @@ -20,6 +20,7 @@ # Boston, MA 02110-1301, USA. # +import sys, time, random, numpy from gnuradio import gr, gr_unittest class test_nlog10(gr_unittest.TestCase): |