Difference between revisions of "Breaking the Ice"
m |
(Write values) |
||
(8 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | + | My notes taken while I was creating a custom client to a gr-ctrlport enabled flow graph. | |
== Enable gr-ctrlport == | == Enable gr-ctrlport == | ||
Line 15: | Line 15: | ||
Test that gr-ctrlport is working properly by running the gr-blocks/examples/ctrlport/pfb_sync_test-qt.grc application in the source tree and the PREFIX/bin/gr-ctrlport-monitor python application (started automatically by pfb_sync_test-qt). | Test that gr-ctrlport is working properly by running the gr-blocks/examples/ctrlport/pfb_sync_test-qt.grc application in the source tree and the PREFIX/bin/gr-ctrlport-monitor python application (started automatically by pfb_sync_test-qt). | ||
+ | |||
+ | == Hello gr-ctrlport == | ||
+ | |||
+ | The [http://doc.zeroc.com/display/Ice/Ice+Manual Ice Manual] contains a [http://doc.zeroc.com/pages/viewpage.action?pageId=5048454 Hello World application] that we can use as template. Read that chapter and try the example. | ||
+ | |||
+ | Translate gnuradio-runtime/lib/gnuradio.ice to C++ code: | ||
+ | |||
+ | $ slice2cpp gnuradio.ice | ||
+ | |||
+ | This will generate a gnuradio.h and gnuradio.cpp file. | ||
+ | |||
+ | Create a client.cpp file that can connect to gnuradio: | ||
+ | |||
+ | #include <Ice/Ice.h> | ||
+ | #include <gnuradio.h> | ||
+ | |||
+ | using namespace std; | ||
+ | using namespace GNURadio; | ||
+ | |||
+ | int main(int argc, char* argv[]) | ||
+ | { | ||
+ | int status = 0; | ||
+ | Ice::CommunicatorPtr ic; | ||
+ | |||
+ | try | ||
+ | { | ||
+ | ic = Ice::initialize(argc, argv); | ||
+ | '''Ice::ObjectPrx base = ic->stringToProxy("gnuradio:tcp -h localhost -p 43243");''' | ||
+ | |||
+ | '''ControlPortPrx ctrlport = ControlPortPrx::checkedCast(base);''' | ||
+ | |||
+ | '''if (!ctrlport)''' | ||
+ | throw "Invalid proxy"; | ||
+ | |||
+ | } | ||
+ | catch (const Ice::Exception& ex) | ||
+ | { | ||
+ | cerr << ex << endl; | ||
+ | status = 1; | ||
+ | } | ||
+ | catch (const char* msg) | ||
+ | { | ||
+ | cerr << msg << endl; | ||
+ | status = 1; | ||
+ | } | ||
+ | |||
+ | if (ic) | ||
+ | ic->destroy(); | ||
+ | |||
+ | return status; | ||
+ | } | ||
+ | |||
+ | The above code will do nothing except establish a connection. | ||
+ | |||
+ | You can put the code below into a script for easy compilation of the client.cpp: | ||
+ | |||
+ | c++ -fpermissive -I. -I$ICE_HOME/include -c gnuradio.cpp client.cpp | ||
+ | c++ -o client gnuradio.o client.o -L$ICE_HOME/lib -lIce -lIceUtil | ||
+ | |||
+ | The -fpermissive flag is necessary because we will be casting from base classes to derived classes. | ||
+ | |||
+ | == Get list of parameters (aka. knobs) == | ||
+ | |||
+ | Reverse engineered from gnuradio.h | ||
+ | |||
+ | We can use ControlPortPrx::properties to get a list of knobs and their properties: | ||
+ | |||
+ | class ControlPort : virtual public ::Ice::Object | ||
+ | { | ||
+ | public: | ||
+ | ... | ||
+ | virtual ::GNURadio::KnobPropMap properties(const ::GNURadio::KnobIDList&, const ::Ice::Current& = ::Ice::Current()) = 0; | ||
+ | |||
+ | We ignore Ice::Current for now. The other GNURadio types are defined as: | ||
+ | |||
+ | typedef ::std::vector< ::std::string> KnobIDList; | ||
+ | typedef ::std::map< ::std::string, ::GNURadio::KnobProp> KnobPropMap; | ||
+ | |||
+ | If we use an empty KnobIDList we will get a map containing all exported knobs. The following client.cpp update fetches this list and prints map key represented by the string: | ||
+ | |||
+ | #include <Ice/Ice.h> | ||
+ | #include <gnuradio.h> | ||
+ | |||
+ | using namespace std; | ||
+ | using namespace GNURadio; | ||
+ | |||
+ | int main(int argc, char* argv[]) | ||
+ | { | ||
+ | int status = 0; | ||
+ | Ice::CommunicatorPtr ic; | ||
+ | |||
+ | '''GNURadio::KnobIDList empty_list; // vector<string>''' | ||
+ | '''GNURadio::KnobPropMap knob_props; // map<string, GNURadio::KnobProp>''' | ||
+ | |||
+ | try | ||
+ | { | ||
+ | // Get proxy object | ||
+ | ic = Ice::initialize(argc, argv); | ||
+ | Ice::ObjectPrx base = ic->stringToProxy("gnuradio:tcp -h localhost -p 43243"); | ||
+ | ControlPortPrx ctrlport = ControlPortPrx::checkedCast(base); | ||
+ | |||
+ | if (!ctrlport) | ||
+ | throw "Invalid proxy"; | ||
+ | |||
+ | '''// Get list of knobs''' | ||
+ | '''knob_props = ctrlport->properties(empty_list);''' | ||
+ | '''cout << "Exported knobs: " << knob_props.size() << endl;''' | ||
+ | |||
+ | '''for (KnobPropMap::iterator it = knob_props.begin(); it != knob_props.end(); ++it)''' | ||
+ | '''cout << " " << it->first << endl;''' | ||
+ | |||
+ | } | ||
+ | catch (const Ice::Exception& ex) | ||
+ | { | ||
+ | cerr << ex << endl; | ||
+ | status = 1; | ||
+ | } | ||
+ | catch (const char* msg) | ||
+ | { | ||
+ | cerr << msg << endl; | ||
+ | status = 1; | ||
+ | } | ||
+ | |||
+ | if (ic) | ||
+ | ic->destroy(); | ||
+ | |||
+ | return status; | ||
+ | } | ||
+ | |||
+ | Knob properties are available in GNURadio::KnobProp. This is a struct with the following fields: | ||
+ | |||
+ | struct KnobProp | ||
+ | { | ||
+ | ::GNURadio::KnobType type; | ||
+ | ::std::string units; | ||
+ | ::std::string description; | ||
+ | ::Ice::Int display; | ||
+ | ::GNURadio::KnobPtr min; | ||
+ | ::GNURadio::KnobPtr max; | ||
+ | ::GNURadio::KnobPtr defaultvalue; | ||
+ | ... | ||
+ | }; | ||
+ | |||
+ | plus a bunch of relational operators and __read() / __write() methods that AFAIK we are not supposed to use. | ||
+ | |||
+ | The properties contain only static information and not the actual value of a knob. | ||
+ | |||
+ | == Read and write values == | ||
+ | |||
+ | To read and write values we must retrieve the KnobMap and then cast the individual knobs to a typed knob and use the value field. In the following example we assume that there is a knob of integer type and a knob of type double: | ||
+ | |||
+ | ... | ||
+ | GNURadio::KnobMap knob_map; // map<string, GNURadio::KnobPtr> | ||
+ | GNURadio::KnobPtr knob; | ||
+ | GNURadio::KnobIPtr knobi; | ||
+ | GNURadio::KnobDPtr knobd; | ||
+ | |||
+ | knob_map = ctrlport->get(empty_list); | ||
+ | |||
+ | // get integer value | ||
+ | knob = knob["key_to_int"]; | ||
+ | knobi = static_cast<KnobIPtr>(knob); | ||
+ | std::cout << "Int value: " << knobi->value << std::endl; | ||
+ | |||
+ | // get double value | ||
+ | knob = knob["key_to_double"]; | ||
+ | knobd = static_cast<KnobDPtr>(knob); | ||
+ | std::cout << "Double value: " << knobd->value << std::endl; | ||
+ | |||
+ | Writing is similar to reading. Since we only used pointers above, modifying knobd->value will also modify the value stored in the knob_map: | ||
+ | |||
+ | knob_map.erase("flowgraph0::max nouptut_items"); // this is read only and would give an error if we tried to write | ||
+ | knobd->value = 20.0; | ||
+ | ctrlport->set(knob_map); | ||
+ | |||
[[Category:GNU Radio]] | [[Category:GNU Radio]] |
Latest revision as of 21:16, 5 April 2013
My notes taken while I was creating a custom client to a gr-ctrlport enabled flow graph.
Contents
Enable gr-ctrlport
Add to ~/.gnuradio/config.conf
[ControlPort] on = True edges_list = False config = /home/directory/.gnuradio/ctrlport.conf
Next, create ~/.gnuradio/ctrlport.conf (based on PREFIX/etc/gnuradio/ctrlport.conf.example):
ControlPort.Endpoints = tcp -t 300 -h 127.0.0.1 -p 43243
Test that gr-ctrlport is working properly by running the gr-blocks/examples/ctrlport/pfb_sync_test-qt.grc application in the source tree and the PREFIX/bin/gr-ctrlport-monitor python application (started automatically by pfb_sync_test-qt).
Hello gr-ctrlport
The Ice Manual contains a Hello World application that we can use as template. Read that chapter and try the example.
Translate gnuradio-runtime/lib/gnuradio.ice to C++ code:
$ slice2cpp gnuradio.ice
This will generate a gnuradio.h and gnuradio.cpp file.
Create a client.cpp file that can connect to gnuradio:
#include <Ice/Ice.h> #include <gnuradio.h> using namespace std; using namespace GNURadio; int main(int argc, char* argv[]) { int status = 0; Ice::CommunicatorPtr ic; try { ic = Ice::initialize(argc, argv); Ice::ObjectPrx base = ic->stringToProxy("gnuradio:tcp -h localhost -p 43243"); ControlPortPrx ctrlport = ControlPortPrx::checkedCast(base); if (!ctrlport) throw "Invalid proxy"; } catch (const Ice::Exception& ex) { cerr << ex << endl; status = 1; } catch (const char* msg) { cerr << msg << endl; status = 1; } if (ic) ic->destroy(); return status; }
The above code will do nothing except establish a connection.
You can put the code below into a script for easy compilation of the client.cpp:
c++ -fpermissive -I. -I$ICE_HOME/include -c gnuradio.cpp client.cpp c++ -o client gnuradio.o client.o -L$ICE_HOME/lib -lIce -lIceUtil
The -fpermissive flag is necessary because we will be casting from base classes to derived classes.
Get list of parameters (aka. knobs)
Reverse engineered from gnuradio.h
We can use ControlPortPrx::properties to get a list of knobs and their properties:
class ControlPort : virtual public ::Ice::Object { public: ... virtual ::GNURadio::KnobPropMap properties(const ::GNURadio::KnobIDList&, const ::Ice::Current& = ::Ice::Current()) = 0;
We ignore Ice::Current for now. The other GNURadio types are defined as:
typedef ::std::vector< ::std::string> KnobIDList; typedef ::std::map< ::std::string, ::GNURadio::KnobProp> KnobPropMap;
If we use an empty KnobIDList we will get a map containing all exported knobs. The following client.cpp update fetches this list and prints map key represented by the string:
#include <Ice/Ice.h> #include <gnuradio.h> using namespace std; using namespace GNURadio; int main(int argc, char* argv[]) { int status = 0; Ice::CommunicatorPtr ic; GNURadio::KnobIDList empty_list; // vector<string> GNURadio::KnobPropMap knob_props; // map<string, GNURadio::KnobProp> try { // Get proxy object ic = Ice::initialize(argc, argv); Ice::ObjectPrx base = ic->stringToProxy("gnuradio:tcp -h localhost -p 43243"); ControlPortPrx ctrlport = ControlPortPrx::checkedCast(base); if (!ctrlport) throw "Invalid proxy"; // Get list of knobs knob_props = ctrlport->properties(empty_list); cout << "Exported knobs: " << knob_props.size() << endl; for (KnobPropMap::iterator it = knob_props.begin(); it != knob_props.end(); ++it) cout << " " << it->first << endl; } catch (const Ice::Exception& ex) { cerr << ex << endl; status = 1; } catch (const char* msg) { cerr << msg << endl; status = 1; } if (ic) ic->destroy(); return status; }
Knob properties are available in GNURadio::KnobProp. This is a struct with the following fields:
struct KnobProp { ::GNURadio::KnobType type; ::std::string units; ::std::string description; ::Ice::Int display; ::GNURadio::KnobPtr min; ::GNURadio::KnobPtr max; ::GNURadio::KnobPtr defaultvalue; ... };
plus a bunch of relational operators and __read() / __write() methods that AFAIK we are not supposed to use.
The properties contain only static information and not the actual value of a knob.
Read and write values
To read and write values we must retrieve the KnobMap and then cast the individual knobs to a typed knob and use the value field. In the following example we assume that there is a knob of integer type and a knob of type double:
... GNURadio::KnobMap knob_map; // map<string, GNURadio::KnobPtr> GNURadio::KnobPtr knob; GNURadio::KnobIPtr knobi; GNURadio::KnobDPtr knobd; knob_map = ctrlport->get(empty_list); // get integer value knob = knob["key_to_int"]; knobi = static_cast<KnobIPtr>(knob); std::cout << "Int value: " << knobi->value << std::endl; // get double value knob = knob["key_to_double"]; knobd = static_cast<KnobDPtr>(knob); std::cout << "Double value: " << knobd->value << std::endl;
Writing is similar to reading. Since we only used pointers above, modifying knobd->value will also modify the value stored in the knob_map:
knob_map.erase("flowgraph0::max nouptut_items"); // this is read only and would give an error if we tried to write knobd->value = 20.0; ctrlport->set(knob_map);