View on GitHub

newsched

The GNU Radio 4.0 Runtime Proof Of Concept

Parameter Access Mechanisms

Throughout GR3.x there is an inconsistent and/or manual way of changing the parameters of a block. It is desired to have all of the possible access mechanisms consistent and consolidated, which include:

Each of these ways of changing a variable in a block must be done manually and is inconsistently handled across blocks in the library. Also, by consolidating the access, we can pipe changes through the scheduler when running, so there is no conflict between, say, the setters and the work() function - removing the need for mutexes. Let’s look at how we can bring all this together (as done in newsched currently).

First, we utilize the new PMT library to represent each “parameter” of a block as a PMT. The block class keeps these PMTs accessible by an id which is autogenerated as an enum (and mappable to/from string) in the top level myblock.h class. So we have a way to store a generic object (PMT) now that is accessible from many different places.

In block.h, a class holding shared pointers to these parameter PMTs is defined:

parameter_config d_parameters;

struct parameter_config {
    std::map<std::string, pmt_sptr> param_map;
    std::map<int, pmt_sptr> param_map_int;
    size_t num() { return param_map.size(); }
    void add(const std::string& name, int id, pmt_sptr b)
    {
        param_map[name] = b;
        param_map_int[id] = b;
    }
    pmt_sptr get(const std::string& name) { return param_map[name]; }
    pmt_sptr get(int id) { return param_map_int[id]; }
    void clear() { param_map.clear(); }
};

We will use the parameters from a multiply_const block to show how parameters can be accessed. This block has 2 parameters defined in the yaml:

parameters:
-   id: k
    label: Constant
    dtype: T
    settable: true
-   id: vlen
    label: Vec. Length
    dtype: size_t
    default: 1

The above yaml gets autogenerated to make multiply_const.{h,cc} which configures the parameters without any intervention from the user.

In multiply_const.h:

    virtual void set_k(T k);         
    virtual T k();
    enum params : uint32_t { id_k,id_vlen, num_params };
    pmt_sptr param_k;
    pmt_sptr param_vlen;

and in multiply_const.cc:

    d_param_str_map = { {"k", id_k},{"vlen", id_vlen},};
    d_str_param_map = { {id_k, "k"},{id_vlen, "vlen"},};
    param_k = std::make_shared<pmtf::pmt>(args.k);
    add_param("k", d_param_str_map["k"], param_k);
    param_vlen = std::make_shared<pmtf::pmt>(args.vlen);
    add_param("vlen", d_param_str_map["vlen"], param_vlen);
}

all this boilerplate for free – nice, right?

Let’s look more at how this fits into all the places where parameters have to be configured by the developer

Block Constructor

The first change we have made with the block constructor is to lump all the parameters that will be set into a struct (that is autogenerated in the top level header). The yaml above generates the following struct:

    struct block_args {
        T k;
        size_t vlen = 1;
    };

So the block constructor (and make factory method) can just use this struct. This prevents changes to the yaml from forcing the developer to change the constructor in several places.

It is not necessary now in the constructor to set private variables for simple values as the PMTs of the base class hold the value and can be accessed from the work function.

The only thing to note, is that in the constructor, setting internal variables for things that get set from constructor args need to use “args.”: d_my_var = args.my_var

Setters and Getters

For each parameter that is “settable” (not default) and/or “gettable” (default), setter and getter methods are autogenerated at the base class. These trigger the block method “request_parameter_change” or “request_parameter_query” respectively - and if the scheduler is running, trigger a callback to get this in between work calls

The Setters and Getters (or the on_parameter_change callbacks) can be overridden in the block implementation to do things like updating NCOs and such.

But the following code is autogenerated in the base block class (e.g. for multiply_const, in multiply_const.cc)

// Settable Parameters
template <class T>
void multiply_const<T>::set_k(T k)
{
    return request_parameter_change(params::id_k, k);
}

template <class T>
T multiply_const<T>::k()
{
    return pmtf::get_as<T>(request_parameter_query(params::id_k));
}

Tags

Not currently implemented, but a common tag should trigger the update of parameters. Since they are PMTs, a tag that has the name of the parameter and it’s new value should be able to work

The goals with any mechanism to update parameters via tags should consider the following:

Message Ports

Each block by default (via autogenerated code) instantiates a message port named “param_update”, and when receiving a PMT, will update the associated parameter

RPC

Via python bindings, a general block_method can call the method on the specified block (setter or getter)