FrontEnd Interfaces

FrontEnd Interfaces (FEI) specify the interaction between applications and radio hardware. They include allocation, operation, and development of tuner devices within the REDHAWK Core Framework (CF). Tuner devices in this context may be Radio Frequency (RF), Intermediate Frequency (IF), or purely digital tuning equipment or software. Explicit types of tuners are defined, so that devices can be categorized by the capabilities they provide.

Tuner devices can provide individual tuners to other REDHAWK entities through tuner allocation. To allocate an individual tuner, the allocate() function of a device is called with an appropriate allocation structure as the only argument. Devices then allocate physical resources and, once a valid connection has been made, begin flowing data out of the device.

Physical devices often need to be split into multiple logical REDHAWK tuners to fully describe their functionality. Splitting physical devices often involves multiple tuners of the same, or mixed, types.

Common FEI Terminology

Terminology Description
device A REDHAWK device.
FEI device Devices that have a device_kind of FRONTEND and implement one of the FEI IDLs. Typically, GPS and navigation devices fall into this category.
tuner A specific tuner capability in an FEI device.
FEI tuner device Devices that have a device_kind of FRONTEND::TUNER. These devices must implement the TunerControl IDL and contain tuners for allocation.

Required Properties for an FEI Tuner

Name / ID Description
device_kind / DCE:cdc5ee18-7ceb-4ae6-bf4c-31f983179b4d Must be set to FRONTEND or FRONTEND::TUNER.
device_model / DCE:0f99b2e4-9903-4631-9846-ff349d18ecfb Used to specify the model of the hardware device.
frontend_tuner_status / FRONTEND::tuner_status A struct sequence where each struct in the sequence represents a single tuner. The structure is defined further in Tuner Status.

Device Types

The set of FEI devices is:

The following sections describe each device type in detail. The provided table describes the ports that are expected in each device type. The last column refers to a new allocation call that REDHAWK devices support that provides the caller with feedback; more on this later.

ANTENNA

The ANTENNA device is an analog receiver.

Minimum ports:

Type name purpose direction description returned by allocate()
RFSource RFSource_in data provides (input) Set of RF inputs that the antenna could switch to and the current RF input No
RFInfo RFInfo_out data uses (output) RF information from the antenna to follow-on receivers No

RX

The RX device is an analog receiver.

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_in data provides (input) RF information from the antenna, such as rf_flow_id No
RFInfo RFInfo_out data uses (output) RF information from the device to follow-on digitizers No
AnalogTuner or AnalogScanningTuner < any > control provides (input) RF control such as setting center frequency Yes

RX_ARRAY

A device that aggregates multiple coherent receivers (RX, ARDC, or ABOT)

Minimum ports:

Type name purpose direction description returned by allocate()
DigitalTuner or DigitalScanningTuner < any > control provides (input) RF control such as setting center frequency Yes

DBOT (Digital input Bank of Tuners)

A device that contains a bank of tuners. The input for this device is a digital feed. Digitized channels are output through child devices (RDC, SRDC, or DRDC).

Minimum ports:

Type name purpose direction description returned by allocate()
DigitalTuner or DigitalScanningTuner < any > control provides (input) RF control such as setting center frequency Yes
Bulk IO < any > data provides (input) Wideband digital feed into the device. Yes

ABOT (Analog input Bank of Tuners)

A device that contains a bank of tuners. The input for this device is an analog feed. Digitized channels are output through child devices (RDC, SRDC, or DRDC).

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_in data provides (input) RF information from the antenna, such as rf_flow_id No
RFInfo RFInfo_out data uses (output) RF information from the device to follow-on digitizers No
DigitalTuner or DigitalScanningTuner < any > control provides (input) RF control such as setting center frequency Yes

ARDC (Analog Receive Digital Channel)

An RX device with a wideband digitized output feed

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_in data provides (input) RF information from the antenna, such as rf_flow_id No
RFInfo RFInfo_out data uses (output) RF information from the device to follow-on digitizers No
DigitalTuner or DigitalScanningTuner < any > control provides (input) RF control such as setting center frequency Yes
Bulk IO < any > data uses (output) Instance-specific wideband data feed. This is typically the whole RF bandwidth seen by the receiver. Yes

RDC (Receive Digital Channel)

RDC is a single received continuously digitized physical channel.

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_in data provides (input) Optional. RF information from the RX, ABOT, or DBOT (flow-through from the antenna), such as rf_flow_id No
DigitalTuner or DigitalScanningTuner < any > control provides (input) RF control such as setting center frequency Yes
Bulk IO < any > data uses (output) Digital data feed Yes

This device only exists as a child of either ABOT or DBOT

SRDC (Snapshot Receive Digital Channel)

SRDC is a single received snapshot digitized physical channel.

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_in data provides (input) Optional. RF information from the RX, ABOT, or DBOT (flow-through from the antenna), such as rf_flow_id No
DigitalTuner or DigitalScanningTuner < any > control provides (input) RF control such as setting center frequency Yes
Bulk IO < any > data uses (output) Digital data feed Yes

This device only exists as a child of either ABOT or DBOT

DRDC (Delay Receive Digital Channel)

DRDC is version of RDC that produced a single received time-delayed, continuously digitized physical channel.

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_in data provides (input) Optional. RF information from the RX, ABOT, or DBOT (flow-through from the antenna), such as rf_flow_id No
DigitalTuner or DigitalScanningTuner < any > control provides (input) RF control such as setting center frequency Yes
Bulk IO < any > data uses (output) Digital data feed Yes

This device only exists as a child of either ABOT or DBOT

TX

The TX device is an analog receiver.

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_in data provides (input) RF information from the processing flow No
RFInfo RFInfo_out data uses (output) RF information for the antenna No
AnalogTuner < any > control provides (input) RF control such as setting center frequency Yes

TX_ARRAY

A device that aggregates multiple coherent transmitters (TX)

Minimum ports:

Type name purpose direction description returned by allocate()
AnalogTuner < any > control provides (input) RF control such as setting center frequency Yes

TDC (Transmit Digital Channel)

TDC is a single transmit digitized physical channel.

Minimum ports:

Type name purpose direction description returned by allocate()
RFInfo RFInfo_out data uses (output) RF information for the TX (flow-through to the antenna), such as rf_flow_id No
TransmitControl < any > control provides (input) RF control such as setting center frequency Yes
Bulk IO < any > data provides (input) Digital data feed to be transmitted Yes

Note: If a system has a single TDC, then there is an implied TX stage, and the TDC can be connected directly to the antenna.

Multi-Channel Frontend Devices

Multi-channel devices provide a single point where different, often independent, RF devices operate. However, these devices are bound in the same enclosure, making them inter-linked in arbitrary ways. Further, independent channels may not be fixed in number, as in the case of a channelizer implemented on FPGA.

To model a workflow with multi-channel devices, the concept of child devices was developed for FEI 3.0. With child devices, a single device process space is used to associate multiple device class definitions. An example definition of child devices with respect to a parent device is shown in the following diagram:

Parent/Child Device Definition and Generation

The parent and child devices are defined by three XML files: Software Package Descriptor (SPD), Software Component Descriptor (SCD), and Properties Definition (PRF). The parent SPD file contains a list of the different child SPD files. The generated code is a single project that contains all device classes.

Devices are deployed programmatically at runtime using the addChild method in the Device base class. The addChild method is invoked on the logical parent of the child device. For example, suppose that XML is defined for device "my_parent", "child_class_a", and "child_class_b". In C++, the children classes are generated in their own namespace to avoid naming conflicts in properties and to allow children to have a different frontend tuner status property than the parent. Then code generation would produce the following file tree:

my_parent
|   my_parent.spd.xml
|   my_parent.scd.xml
|   my_parent.prf.xml
|   child_class_a.spd.xml
|   child_class_a.scd.xml
|   child_class_a.prf.xml
|   child_class_b.spd.xml
|   child_class_b.scd.xml
|   child_class_b.prf.xml
|   reconf
|   configure.ac
|   Makefile.am
|
|---cpp
|   |   my_parent.cpp
|   |   my_parent.h
|   |   my_parent_base.cpp
|   |   my_parent_base.h
|   |
|   |---child_class_a
|   |   |   child_class_a.cpp
|   |   |   child_class_a.h
|   |   |   child_class_a_base.cpp
|   |   |   child_class_a_base.h
|   |
|   |---child_class_b
|   |   |   child_class_b.cpp
|   |   |   child_class_b.h
|   |   |   child_class_b_base.cpp
|   |   |   child_class_b_base.h

After expanding the previous example so that child_class_a is the parent of a second instance of child_class_b, the code in my_parent.cpp would contain:

#include "child_class_a/child_class_a.h"
#include "child_class_b/child_class_b.h"
// this device instantiation code was added to the constructor, but it does not need to be added there
void my_parent_i::constructor()
{
    std::string child_class_a_name("child_class_a");
    std::string child_class_b_name("child_class_b");

    child_class_a::child_class_a_i* first_layer_a = this->addChild<child_class_a::child_class_a_i>(child_class_a_name);
    child_class_a::child_class_a_i* first_layer_b = this->addChild<child_class_a::child_class_a_i>(child_class_b_name);

    child_class_a::child_class_a_i* second_layer_b = first_layer_a->addChild<child_class_a::child_class_a_i>(child_class_b_name);
}

Each Device has a member named _dynamicComponents, with handles to each of its children. Below is an example of using _dynamicComponents:

for (unsigned int i=0; i<_dynamicComponents.size(); i++) {
    child_class_a::child_class_a_i* base_dev = dynamic_cast<child_class_a::child_class_a_i*>(_dynamicComponents[i]);
    std::cout<<"  "<<_dynamicComponents[i]->_identifier;
    if (base_dev == NULL) {
        std::cout<<" is not of type child_class_a::child_class_a_i";
    } else {
        std::cout<<" is of type child_class_a::child_class_a_i";
    }
    std::cout<<std::endl;
}

The vector _dynamicComponents is public, so a particular device's children can be inspected by other classes. Thread-safe changes to _dynamicComponents are managed through the DynamicComponent base class's _dynamicComponentDeploymentLock member.

Through the Aggregate device interface, each device has access to its parent and children. The following code demonstrates this access with a different arrangement of devices.

from ossie.utils import redhawk
dom = redhawk.attach()
DeviceA = None
for dev in dom.devMgrs[0].devs:
    if dev.name == 'DeviceA':
        DeviceA = dev
        break

DeviceB = DeviceA.devices[0]

DeviceC = DeviceB.devices[0]
DeviceD = DeviceB.devices[1]

up_DeviceB = DeviceC.compositeDevice
up_DeviceA = up_DeviceB.compositeDevice

The relationship above is shown graphically, below:

Navigating Parent/Child Devices

Device Kinds

The device_kind property is used to ease the identification of candidate devices, reducing the search scope. The possible device_kind property values that should be used is as follow:

value description
FRONTEND::PARENT This device has FEI children
FRONTEND This is an FEI device
FRONTEND::GPS Device that originates GPS information
FRONTEND::NAVIGATION Device that originates Navigation information
FRONTEND::RFINFO Device that originates RF Info (i.e.: an antenna)

SRI and Keywords

FrontEnd devices use both the basic BulkIO SRI fields and the Keywords field for additional items. All standard SRI fields are filled out as appropriate in accordance with the existing BulkIO IDL descriptions. The following table describes the specific keywords for FrontEnd devices.

Name Type Description Notes
COL_RF double Collector center frequency in Hz. Center frequency of the collector, which is typically thought of as the center frequency of the wideband receiver used to generate the IF data. In the case of an RDC, the value of COL_RF is the center frequency of the input to the RX.
CHAN_RF double Channel center frequency in Hz. The center frequency of the stream. The value of CHAN_RF is equal to the COL_RF for RX, ABOT, or DBOT tuners but should still be included.
FRONTEND::BANDWIDTH double Effective bandwidth in Hz. The effective bandwidth of the stream.
FRONTEND::RF_FLOW_ID string RF Flow ID of data. Always include even if the RF Flow ID is blank.
FRONTEND::DEVICE_ID string The ID of the device. Component ref ID, which allows downstream users to gain a reference to the device that created the data.
FRONTEND::ALLOCATION_ID string The allocation ID for this data. In cases where the data in the stream is generated by a FrontEnd device, this is allocation ID corresponding to the generated data
FRONTEND::PATHDELAY double Path delay for the RF data Measured RF path delay (from calibration)
FRONTEND::TDOA_SIGMA double Time difference of arrival sigma in seconds Time difference of arrival sigma at this tuned frequency
FRONTEND::FDOA_SIGMA double Frequency difference of arrival sigma in Hz Frequency difference of arrival sigma at this tuned frequency

Parent/Child FEI Devices

In the context of FEI, it is possible to programmatically create device proxies that model the underlying hardware. For example, in the case where the configuration of the underlying RF hardware is not static and an API exists to scan the hardware, the following CONOP is enabled by the addChild API:

Sophisticated Parent/Child FEI Devices

As seen in the example above, the underlying hardware is a transceiver with two receive channels and one transmit channel. Each devices is quasi-independent, sharing some common device control functionality, but each subsystem operating in a semi-independent fashion. Also notice that because the RDC share a single receiver, they need a common ABOT or DBOT, while the TDC does not require a TX device because there is only one TDC.

Note: the parent device (WB Transceiver and Channelizer) is not an FEI device. This device is a custom device that aggregates the underlying devices; in this case ABOT, RDC, and TDC. The parent device delegates the allocations to the child devices and can promote either ports or properties of the different child devices. Because allocations are returned with a reference to the child device satisfying the allocation (more on this in the next section), the parent device can be a custom device provided for convenience.

Allocations with feedback

Devices' capacity management interface has been extended to provide better support to sophisticated multi-device allocations. Single allocations can now be made through the allocate() call. The allocate() method is defined as follows:

struct PortDescription {
    Object port_ref;            // port object
    string name;                // port name
    string repository_id;       // port interface id (e.g.: "IDL:BULKIO/dataFloat:1.0")
};

typedef sequence <PortDescription> PortDescriptions;

struct Allocation {
    CF::Device device_ref;              // device that satisfied the allocation (the allocation could have been delegated)
    PortDescriptions data_ports;        // data ports ('provides' for TX, 'uses' for RX)
    PortDescriptions control_ports;     // control ports
    CF::Properties allocated;           // what allocation values the device met
    string alloc_id;                    // unique identifier (used to deallocate)
};
typedef sequence <Allocation> Allocations;

Allocations allocate(in CF::Properties capacities);

The allocate() method takes a sequence of properties as its argument and returns a sequence of Allocation structures. Each structure contains:

The Allocation structure returns the actual allocations because they may differ from the request. For example, in the case of a tuner, while a request may be for 12 kHz of bandwidth, if the device can only be configured to 12.5 kHz bandwidth, then the returned allocation value is 12.5 kHz. The amount of divergence between the requested and the actual value for the device parameter is controlled by the "tolerance" value in the allocation request. For example, if the requested bandwidth is 12 kHz and the tolerance is set to 10 (the value is in percent), then the maximum allowable returned value is 13.2 kHz.

All the structures returned in a single allocate() call relate to the same allocation. For example, if a coherent receive array is requested, the return value would contain an Allocation structure instance for each receive tuner that makes up the receive array. The unique allocation id, generated by the device responding to the allocation, populates every Allocation structure returned on the allocate() call. The receiver array example is described in more detail in the next section.

The data port reference is the data port associated with the allocation. For example, for a RDC allocation, the data port corresponds to the tuner's output port. Conversely, for a TDC allocation, the data port corresponds to the digital up-converter's data input port (to be transmitted over the air). If the allocation does not have a corresponding data transport, as in the case of generic processing resources, the data port reference is nil.

Like the data port, the control port reference is for the control port associated with the allocation. In the case of FEI, this could be a DigitalTuner port or a ScanningDigitalTuner port. If the allocation does not correspond to a controllable resource, the control port reference is nil.

Deallocation is based on the allocation id returned in the Allocation structures returned by allocate(). The deallocate() call is as follows:

void deallocate(in allocation_id);

All devices that are associated with the given allocation id are deallocated. For example, if a phased array is allocated with n tuners, and the deallocate() call is made on any of the associated devices, then all of them are deallocated. In the case of such allocations where more than one device is allocated (such as in an array), the deallocate() call can be made against one of the single RDC devices to deallocate just that one device and not the others in the initial allocation.

Continuing with the example transceiver in the previous section, a single allocation made to the parent device can return the allocation of a single RDC, as shown below:

Child FEI Allocation

The return value from the allocation to the parent device is the child device's reference as well as its output data port and input control port.

Controlling Coherent Receivers (arrays) with Frontend Interfaces

When ingesting data from coherent tuners, allocations must be coordinated between multiple independent tuners; each of these tuners is associated with a different RF receiver and a different antenna. While each of these tuners is ostensibly independent, they must all be tuned to the same frequency over the same bandwith, with data generated coherently (in lock-step) between the different tuners.

The mechanism for supporting the process has a parent (aggregate) device for the array, and child devices for each receiver. Each receiver has a set of child devices: one for each tuner. In this case, each tuner (RDC FEI device) is a child of a wideband receiver (RX or ABOT FEI device), and each wideband receiver is the child of the array device proxy (RX_ARRAY FEI device).

The wideband receiver is an ABOT if it contains narrowband tuners, ARDC if it is a single channel, and RX if it does not. An ARDC is a combination of ABOT and a single RDC. Multiple receivers (RX, ABOT, or ARDC) can be combined into a coherent set, where the set is controlled by RX_ARRAY.

This device parent hierarchy is shown below:

Device Hierarchy

The programmatic deployment of this hierarchy uses the addChild() method built into each device. For example, the following code in rx_array.cpp shows a single RX_ARRAY (rx_array_i) creating two instances of ABOT (abot_i), each with 2 RDC (rdc_i).

#include "abot/abot.h"
#include "rdc/rdc.h"
// this device instantiation code was added to the constructor, but it does not need to be added there
void rx_array_i::constructor()
{
    std::string abot_name("abot");
    std::string rdc_name("rdc");

    abot_ns::abot_i* abot_1 = this->addChild<abot_ns::abot_i>(abot_name);
    rdc_ns::rdc_i* rdc_1_1 = abot_1->addChild<rdc_ns::rdc_i>(rdc_name);
    rdc_ns::rdc_i* rdc_1_2 = abot_1->addChild<rdc_ns::rdc_i>(rdc_name);

    abot_ns::abot_i* abot_2 = this->addChild<abot_ns::abot_i>(abot_name);
    rdc_ns::rdc_i* rdc_2_1 = abot_2->addChild<rdc_ns::rdc_i>(rdc_name);
    rdc_ns::rdc_i* rdc_2_2 = abot_2->addChild<rdc_ns::rdc_i>(rdc_name);
}

As shown above, the device hierarchy is defined programmatically in the parent device, in this case the RX_ARRAY device. This relationship can be shown programmatically from the Python sandbox. For example, assuming that the Python session is attached to a domain with a deployment of the devices and node included in this project, the following code can be used to retrieve the array, the abot receivers, and the single-channel rdc tuners:

from ossie.utils import redhawk
dom = redhawk.attach()
agg_dev = None
for dev in dom.devMgrs[0].devs:
    if dev.name == 'rx_array':
        agg_dev = dev
        break
rx_d_1 = agg_dev.devices[0]
rx_d_2 = agg_dev.devices[1]

rdc_in_rx_d_1 = []
for dev in rx_d_1.devices:
    rdc_in_rx_d_1.append(dev)

rdc_in_rx_d_2 = []
for dev in rx_d_2.devices:
    rdc_in_rx_d_2.append(dev)

The parent device can be retrieved from any one child device.

for dev in dom.devMgrs[0].devs:
    if 'rdc' in dev.label:
        rdc_rx = dev
        break
rx_d = rdc_rx.compositeDevice
rx_array = rx_d.compositeDevice

For the array to function correctly, each wideband receiver must be connected to a different antenna. This antenna can be modeled with the GenericAntenna device, and its RFInfo port can be used to pass the rf_flow_id for that specific antenna element. To propagate the rf_flow_id, the rx_digitizer's output RFInfo port needs to be connected to each rdc's input RFInfo port. This functionality is not created by the code generators because it is CONOP-specific and must be implemented by the developer in the port's callback function. Note that this connection does not have to be defined in XML; instead it can be implemented programmatically when the devices are instantiated.

Allocating a tuner

Tuner allocations have remained unchanged from FEI 2.0.

ID type description
FRONTEND::tuner_allocation::tuner_type string Device type
FRONTEND::tuner_allocation::allocation_id string Requested allocation id (empty string if does not matter). Will cause an error if not available
FRONTEND::tuner_allocation::center_frequency double Requested center frequency
FRONTEND::tuner_allocation::bandwidth double Requested bandwidth
FRONTEND::tuner_allocation::bandwidth_tolerance double Allowable percent above requested bandwidth (e.g.: 100 would be twice)
FRONTEND::tuner_allocation::sample_rate double Requested sample rate
FRONTEND::tuner_allocation::sample_rate_tolerance double Allowable percent above requested sample rate
FRONTEND::tuner_allocation::device_control boolean True: Has control over the device to make changes, False: marked as listening to device and wants to be notified when de-allocated
FRONTEND::tuner_allocation::group_id string Unique identifier that specifies the group a device must be in. Must match group_id on the device
FRONTEND::tuner_allocation::rf_flow_id string Specifies the RF flow of a specific input source to allocate against. Empty if the rf_flow_id does not matter

Listener allocations are not needed (since there is a single data port per channel), but are still supported.

ID type description
FRONTEND::listener_allocation::existing_allocation_id string Allocation to listen to
FRONTEND::listener_allocation::listener_allocation_id string Listener allocation

Allocation extensions

The following allocations can be passed along with FRONTEND::tuner_allocation to specialize the allocation_id

The FRONTEND::snapshot allocation is used to specify snapshot parameters. The response to an allocation including FRONTEND::snapshot is an SRDC device

ID type description
FRONTEND::snapshot_start::whole_seconds double J1970 GMT
FRONTEND::snapshot_start::partial_seconds double 0.0 to 1.0
FRONTEND::snapshot_stop::whole_seconds double J1970 GMT
FRONTEND::snapshot_stop::partial_seconds double 0.0 to 1.0

The FRONTEND::delay allocation is used to specify time-delay parameters for the data feed. The response to an allocation including FRONTEND::delay is an DRDC device

ID type description
FRONTEND::delay::whole_seconds double J1970 GMT
FRONTEND::delay::partial_seconds double 0.0 to 1.0
FRONTEND::delay::relative boolean True if relative to now, false if absolute (UTC)

The FRONTEND::stream_id allocation is used to request a particular stream id for the generated data sets. The response to an allocation including FRONTEND::strema_id is an RDC, ARDC, SRDC, or DRDC device

ID type description
FRONTEND::stream_id::requested_stream_id string Requested stream id for generated data

The FRONTEND::data_format allocation is used to request a particular data type for the generated data.

ID type description
FRONTEND::data_format::bulkio_type string Repository ID for the requested type (e.g.: IDL:BULKIO/dataShort:1.0)
FRONTEND::data_format::mode boolean Set true for complex, false for real

The FRONTEND::payload_format allocation is used to request the payload in the case of SDDS or VITA49 data sets

ID type description
FRONTEND::payload_format::native_type string binary format (e.g.: float, short, short, ushort)
FRONTEND::payload_format::endian boolean Set true for big endian, false for little endian

The FRONTEND::upstream allocation is used to require that the allocated device have a connection from the device specified

ID type description
FRONTEND::upstream::device_id string device instance id for the upstream device

Allocating a set of coherent channels

Unfortunately, some of the data structures needed to allocate an array are currently not available in the IDE. So the allocations must be made through a programmatic interface like the Python sandbox.

To allocate an array, the allocation property "FRONTEND::coherent_feeds" of type string sequence was created. The number of string elements in the sequence determines how many tuners (from different wideband feeds) need to be allocated for this request. If the value in each of these strings is not an empty string, then that specific set of rf_flow_id values must be matched. If each string is empty, then any of the wideband feeds can satisfy the request. Note that at most 1 tuner per wideband feed can be allocated in each request.

The following code allocates 2 tuners with 1kHz of bandwidth for rf_flow_id "aperture_1" and "aperture_2".

from ossie.utils import redhawk
import frontend
from ossie.cf import CF
from omniORB import any as _any

dom = redhawk.attach()
agg_dev = None
for dev in dom.devMgrs[0].devs:
    if dev.name == 'rx_array':
        agg_dev = dev
        break

alloc_tuner_1 = frontend.createTunerAllocation(tuner_type='RDC',
                                               allocation_id='alloc_id_1',
                                               bandwidth=1000.0,
                                               returnDict=False)
coherent_request = CF.DataType(id='FRONTEND::coherent_feeds',
                               value=_any.to_any(['aperture_1', 'aperture_2']))
allocation_response = agg_dev.allocate([alloc_tuner_1, coherent_request])

Deallocation requires the allocation id:

agg_dev.deallocate(allocation_response[0].alloc_id)

The following code allocate 2 tuners with 1kHz of bandwidth for any rf_flow_id.

alloc_tuner_1 = frontend.createTunerAllocation(tuner_type='RDC',
                                               allocation_id='alloc_id_1',
                                               bandwidth=1000.0,
                                               returnDict=False)
coherent_request_any_feed = CF.DataType(id='FRONTEND::coherent_feeds',
                                        value=_any.to_any(['', '']))
allocation_response = agg_dev.allocate([alloc_tuner_1, coherent_request_any_feed])

To allocate scanning tuners, include a scanner allocation request. For example, assuming that a scanning functionality is needed where the scanner is expected to change frequencies above 10kHz, use the following:

alloc_scanner = frontend.createScannerAllocation(min_freq=10000.0, returnDict=False)
alloc_tuner_1 = frontend.createTunerAllocation(tuner_type='RDC',
                                               allocation_id='alloc_id_1',
                                               bandwidth=1000.0,
                                               returnDict=False)
coherent_request_any_feed=CF.DataType(id='FRONTEND::coherent_feeds', value=_any.to_any(['', '']))
allocation_response = agg_dev.allocate([alloc_tuner_1, coherent_request_any_feed])

If scanning functionality is needed, then each device's scan plan needs to be set independently, where the scan strategy is set for each device separately and the scan start time is set to some arbitrary time in the future.

Command and Control

Command and Control of existing allocated tuners is performed through the DigitalTuner or AnalogTuner port on the FEI device. These commands allow external users to get and set specific settings for each of the tuners. Each FEI tuner device must have a DigitalTuner port named DigitalTuner_in (or AnalogTuner_in for an AnalogTuner port) that allows for command and control. All of the functions in the tuner control interface need to be implemented even if only to report that the capability is not supported. Each of these tuner control interface functions uses the Allocation ID to uniquely identify the tuners.

NOTE: An output control port used to control an FEI device follows the same connectivity rules explained in Custom IDL Interfaces.

Tuner Control Interface

The tuner control interface describes two interfaces for control. The first is the AnalogTuner, which describes all of the functions common for Digital and Analog tuners. DigitalTuner inherits AnalogTuner and adds setOutputSampleRate() and getOutputSampleRate().

Tuner Control Functions

The following table describes the functions that can be accessed via the Tuner Control IDL.

Function Prototype Description
string getTunerType(in string id) Get the type of tuner (e.g., RX or DDC) associated with this Allocation ID.
boolean getTunerDeviceControl(in string id) Returns whether this Allocation ID has control (modification privileges) over the tuner.
string getTunerGroupId(in string id) Retrieves the Group ID (may be empty) for this Allocation ID.
string getTunerRfFlowId(in string id) Retrieves the RF Flow ID (may be empty) for this Allocation ID.
CF::Properties getTunerStatus(in string id) Key/Value pair of entire tuner status structure. Note: The return is a sequence of simple properties, not a single struct property.
void setTunerCenterFrequency(in string id, in double freq) Set the current center frequency in Hz.
double getTunerCenterFrequency(in string id) Get the current center frequency in Hz.
void setTunerBandwidth(in string id, in double bw) Set the current bandwidth in Hz.
double getTunerBandwidth(in string id) Get the current bandwidth in Hz.
void setTunerAgcEnable(in string id, in boolean enable) Enable or disable the Auto Gain Control (AGC). True indicates that the AGC should be enabled.
boolean getTunerAgcEnable(in string id) Get the current status of AGC. True indicates enabled.
void setTunerGain(in string id, in float gain) Set tuner gain in dB.
float getTunerGain(in string id) Get current tuner gain in dB.
void setTunerReferenceSource(in string id, in long source) Set the tuner reference source. Zero is defined as internal and one is defined as external.
long getTunerReferenceSource(in string id) Get the current tuner reference source.
void setTunerEnable(in string id, in boolean enable) Set the output enable state of the tuner. True indicates output is enabled.
boolean getTunerEnable(in string id) Get the current output enable state of the tuner. True indicates output is enabled.
void setTunerOutputSampleRate(in string id, in double sr) Set the output sample rate in samples/sec.
double getTunerOutputSampleRate(in string id) Get the output sample rate in samples/sec.
void configureTuner(in string id, in CF::Properties tunerSettings)
CF::Properties getTunerSettings(in string id)

Scanner Tuner Control Functions

The following table describes additional tuner control functions, which are present when the device is of type RX_SCANNER_DIGITIZER.

Function Prototype Description
ScanStatus getScanStatus(in string id) Get the current scanner status. The return structure is of type FRONTEND::ScanningTuner::ScanStatus, which contains information on the scanning strategy, the scheduled start time, the list of center frequencies that the plan will execute, and whether or not the scan has started
void setScanStartTime(in string id, in BULKIO::PrecisionUTCTime start_time) Schedule when a scan plan should start (in epoch time). Setting the time to 0 or a previous time with the tcstatus flag set to true starts a scan immediately. To disable the scan, set the start_time's tcstatus flag to false.
void setScanStrategy(in string id, in ScanStrategy scan_strategy) Provide a plan for what frequencies the scanner will cover and how it will cover them.

Tuner Control Exceptions

The following table describes exceptions that may occur during calls to tuner control functions.

Exception Description Notes
BadParameterException Parameter provided is invalid. Indicates the value provided is out of bounds for the capability of the device or that the value was invalid (e.g., a negative frequency).
NotSupportedException Capability is not supported. Indicates the tuner does not support the setting (or getting) of this capability.
FrontendException Generic FrontEnd exception. Indicates there is a FrontEnd issue preventing the command, often because the Allocation ID does not match any currently allocated tuners.

Scanning Interface

A scanning tuner requires a definition of how it should scan. This is provided with the ScanStrategy data structure.

ScanStrategy Description

The following table describes the members of the ScanStrategy data structure.

Member Type Description
scan_mode ScanMode ScanMode is an enumerated type that can be set to MANUAL_SCAN, SPAN_SCAN, or DISCRETE_SCAN
scan_definition ScanModeDefinition ScanModeDefinition is a union that provides the mode-specific information: double center_frequency for MANUAL_SCAN, ScanSpanRanges freq_scan_list for SPAN_SCAN, and Frequencies discrete_freq_list for DISCRETE_SCAN
control_mode OutputControlMode OutputControlMode is an enumerated type that can be set to TIME_BASED or SAMPLE_BASED
control_value double This is the value for control_mode. The unit is seconds for TIME_BASED and samples for SAMPLE_BASED

ScanSpanRange Description

The data structures Frequencies and ScanSpanRanges provide more detail for their respective modes. Frequencies is a sequence of doubles. ScanSpanRanges is a sequence of ScanSpanRange. The following table describes the members of ScanSpanRange.

Member Type Description
begin_frequency double The beginning center frequency for the scan in Hz
end_frequency double The ending center frequency for the scan in Hz
step double The change in center frequency in Hz

GPS Interface

The GPS IDL provides an interface to retrieve GPS information from a device that is GPS-enabled. The GPS interface is composed of two attributes and no functions. However, the data structures returned by these attributes contain a large amount of detail.

NOTE: This interface's attributes are read/write, therefore, this interface can be used to read GPS information or to receive GPS information.

GPS Attributes

The GPS attributes are listed in the following table.

Attribute Prototype Description
GPSInfo gps_info Get a detailed description of the GPS information on the device.
GpsTimePos gps_time_pos Return the position generated by GPS as well as the timestamp for that position estimate.

GPSInfo

The GPSInfo descriptions are listed in the following table.

Member Type Description
source_id string Device identifier for the device that generated the GPS location report (this device's ID if accessing the hardware directly)
rf_flow_id string Identifier for the RF source (antenna)
mode string "Locked" if the GPS Receiver has locked onto the signal, and "Unlocked" if the GPS Receiver has not locked onto the signal. Use "Tracking" if the GPS Receiver has found but not locked onto the signal (if available).
fom long Position figure-of-merit (refer to the Figure of Merit table)
tfom long Time figure-of-merit (refer to the Time Figure of Merit table)
datumID long Identifier for the reference ellipsoid (datum). Use 47 for WGS 1984, the GPS datum
time_offset double Receiver oscillator's most recent time offset (seconds). Usually 0
freq_offset double Receiver's center frequency offset (Hz)
time_variance double Receiver oscillator's time offset variance (seconds2). Usually 0
freq_variance double Receiver's center frequency offset variance (Hz2)
satellite_count short Number of satellites visible to the receiver
snr float GPS receiver's reported signal to noise ratio. The definition of this value is not standardized and varies by manufacturer.
status_message string Device-specific status message
timestamp BULKIO::PrecisionUTCTime Timestamp for the GPS information
additional_info CF::Properties Device-specific additional information

Figure of Merit

The figure of merit (fom) provides the expected position error (EPE). The following table describes the possible values of the fom.

Value Error (meters)
1 less than 25
2 less than 50
3 less than 75
4 less than 100
5 less than 200
6 less than 500
7 less than 1000
8 less than 5000
9 greater than or equal to 5000

Time Figure of Merit

The time figure of merit (tfom) provides the expected time error (ETE). The following table describes the possible values of the tfom.

Value Error (nanoseconds)
1 less than 1
2 less than 10
3 less than 100
4 less than 1e3
5 less than 1e4
6 less than 1e5
7 less than 1e6
8 less than 1e7
9 greater than or equal to 1e7

GPS Interface Helpers

The GPS Interface helpers identify whether the GPS receiver has locked in on a signal or only found the receiver. The helper details are listed in the table below.

Member Value Description
GPS_MODE_LOCKED Locked GPS Receiver has locked onto the signal
GPS_MODE_UNLOCKED Unlocked GPS Receiver has not locked onto the signal
GPS_MODE_TRACKING Tracking GPS Receiver has found, but not locked onto the signal (optional)

GpsTimePos

The GPSTimePos reports the location and the timestamp associated with the location report. The GPSTimePos details are listed in the table below.

Member Type Description
position PositionInfo Position report
timestamp BULKIO::PrecisionUTCTime Timestamp for the location report

GPS Support Types

The following table lists the type used by GpsTimePos for communicating position information.

Member Type Description
valid boolean The report is valid
datum string Reference ellipsoid. Set to DATUM_WGS84
lat double Latitude (degrees)
lon double Longitude (degrees)
alt double Altitude (meters)

This interface provides basic navigational information such as position, velocity, and so forth. The NavData interface is composed of one attribute and no functions. While the interface returns a single attribute, that attribute contains multiple positional structures, some of which may not be supported by the navigation data source. Structures contain a "valid" flag, allowing the hardware to indicate whether or not the source provides that specific data set.

NOTE: This interface's attributes are read/write, therefore, this interface can be used to read navigation information or to receive navigation information.

The NavData attribute is used to retrieve additional detailed navigation information from devices.

Attribute Prototype Description
NavigationPacket nav_packet Get detailed navigational information from the device.

The NavigationPacket provides all of the detail required for communicating navigational information for a device.

Member Type Description
source_id string Device identifier for the device that generated the navigation report (this device's ID if accessing the hardware directly)
rf_flow_id string Identifier for the RF source (antenna)
position PositionInfo Location information in lat/long/altitude
cposition CartesianPositionInfo Location information in x/y/z
velocity VelocityInfo Velocity vector
acceleration AccelerationInfo Acceleration vector
attitude AttitudeInfo Attitude info (pitch/yaw/roll)
timestamp BULKIO::PrecisionUTCTime Timestamp for the navigation data information
additional_info CF::Properties Device-specific additional information

The following tables list the types that are used by the NavigationPacket for communicating position, velocity, acceleration, and attitude information.

PositionInfo
Member Type Description
valid boolean The report is valid
datum string Reference ellipsoid. Set to DATUM_WGS84
lat double Latitude (degrees)
lon double Longitude (degrees)
alt double Altitude (meters)
CartesianPositionInfo
Member Type Description
valid boolean The report is valid
datum string Reference ellipsoid. Set to DATUM_WGS84
x double X-axis (meters)
y double Y-axis (meters)
z double Z-axis (meters)
VelocityInfo
Member Type Description
valid boolean The report is valid
datum string Reference ellipsoid. Set to DATUM_WGS84
coordinate_system string CS_ECF (Earth-Centered Earth-Fixed), CS_ENU (East, North, Up), CS_NED (North, East, Down)
x double X-axis (meters/second)
y double Y-axis (meters/second)
z double Z-axis (meters/second)
AccelerationInfo
Member Type Description
valid boolean The report is valid
datum string Reference ellipsoid. Set to DATUM_WGS84
coordinate_system string CS_ECF (Earth-Centered Earth-Fixed), CS_ENU (East, North, Up), CS_NED (North, East, Down)
x double X-axis (meters/second^2)
y double Y-axis (meters/second^2)
z double Z-axis (meters/second^2)
AttitudeInfo
Member Type Description
valid boolean The report is valid
pitch double Pitch (degrees)
yaw double Yaw (degrees)
roll double Roll (degrees)

RFInfo Interface

The RFInfo interface describes the contents of an RF feed (antenna/feed); the most direct analogy is that RFInfo describes a wire connecting an antenna to a tuner.

NOTE: This interface's attributes are read/write, therefore, this interface can be used to send RF feed information or to describe the RF feed information being received.

RFInfo Attributes

The RFInfo interface implements the attributes listed in the table below.

Attribute Prototype Description
string rf_flow_id A string that uniquely describes the feed
RFInfoPkt rfinfo_pkt A description of the RF feed

Support Types

The following tables list the types that are used by the RFInfo for communicating RF, sensor, antenna, feed, and frequency, path delay, and frequency range information.

RFInfoPkt
Member Type Description
rf_flow_id string The string that uniquely describes the feed
rf_center_freq double Center frequency for the RF Feed (the physical antenna)
rf_bandwidth double Bandwidth for the RF Feed (the physical antenna)
if_center_freq double Center frequency for the IF that is output by the feed (if the hardware performs a down-conversion)
spectrum_inverted boolean Set to true if the spectrum for the feed output is inverted
sensor SensorInfo A description of the antenna/feed combination
ext_path_delays sequence PathDelay Frequency-selective path delays
capabilities RFCapabilities Potential operating frequency range for the antenna/feed combination
additional_info CF::Properties Device-specific additional information
SensorInfo
Member Type Description
mission string Name for the mission
collector double Name for the collector
rx double Name for the rx element
antenna AntennaInfo Description of the antenna's RF characteristics
feed FeedInfo Description of the feed's RF characteristics
AntennaInfo
Member Type Description
name string Name for the antenna
type string Type of antenna
size string Size of the antenna
description string Description of the antenna installation
FeedInfo
Member Type Description
name string Name for the feed
polarization string Polarization (for example, "vertical", "horizontal")
freq_range FreqRange Operating range for the RF feed
FreqRange
Member Type Description
min_val double Minimum frequency
max_val double Maximum frequency
values sequence double List of specific center frequencies that are available (if not continuous)
PathDelay
Member Type Description
freq double Frequency where this delay applies
delay_ns double Delay at the given frequency (nanoseconds)
RFCapabilities
Member Type Description
freq_range FreqRange Minimum to Maximum center frequency
bw_range FreqRange Minimum to Maximum operating bandwidth

RFSource Interface

The RFSource interface describes a device that contains multiple RF feeds and any one can be selected at any one time. This is equivalent to an RF switch connected to multiple antenna subsystems.

NOTE: This interface's attributes are read/write, therefore, they are meant to be used to control the RF source (RF switch) to select the appropriate RF stream to feed to the tuner.

RFSource Attributes

The RFSource interface implements the attributes listed in the table below.

Attribute Prototype Description
RFInfoPktSequence available_rf_inputs The list of RF feeds that can be selected. Each RF feed is described by an RFInfoPkt structure
RFInfoPkt current_rf_input The RF feed that is currently streaming out of the device

Tuner status

The FRONTEND::tuner_status property provides the status of every allocated FEI device. The FRONTEND::tuner_status property contains both required and optional elements as follows:

Required elements

ID type description
FRONTEND::tuner_status::tuner_type string ANTENNA, RX, RX_ARRAY, DBOT, ABOT, ARDC, RDC, SRDC, DRDC, TX, TX_ARRAY, or TDC
FRONTEND::tuner_status::allocation_id_csv string comma-separated list of allocation ids for this tuner, where the first is the controlling allocation
FRONTEND::tuner_status::center_frequency double Current center frequency in Hz. Actual tuned frequency rather than the desired frequency (if those values are not the same).
FRONTEND::tuner_status::bandwidth double Current bandwidth in Hz. Actual bandwidth rather than the desired bandwidth (if those values are not the same).
FRONTEND::tuner_status::sample_rate double Current sample rate in Hz. Actual sample rate rather than the desired sample rate (if those values are not the same). Can be ignored for such devices as analog tuners
FRONTEND::tuner_status::group_id string Unique ID that specifies a group of devices. Actual Group ID, regardless whether it was requested in the tuner allocation or not.
FRONTEND::tuner_status::rf_flow_id string Specifies a certain RF flow to allocate against. Actual RF Flow ID, regardless whether it was requested in the tuner allocation or not.
FRONTEND::tuner_status::enabled boolean Indicates if tuner is enabled. Enabled refers to the output state not any internal hardware/software state.

Required elements for scanner devices

ID type description
FRONTEND::tuner_status::scan_mode_enabled boolean Describes whether or not a scan plan is running on this tuner.
FRONTEND::tuner_status::supports_scan boolean Describes whether or not this tuner can support a scan plan. Scan plans may not necessarily be available to all tuners in a device

Optional elements

ID type description
FRONTEND::tuner_status::scan_mode_enabled boolean Describes whether or not a scan plan is running on this tuner.
FRONTEND::tuner_status::bandwidth_tolerance double Allowable percentage over requested bandwidth. Tolerance provided by the requester.
FRONTEND::tuner_status::sample_rate_tolerance double Allowable percentage over requested sample rate. Tolerance provided by the requester.
FRONTEND::tuner_status::complex boolean Indicates if the output data is complex. True for complex; False for real.
FRONTEND::tuner_status::gain double Current gain in dB
FRONTEND::tuner_status::agc boolean Indicates if the tuner has AGC enabled. Even if AGC is enabled, the device still reports the current gain in the gain property.
FRONTEND::tuner_status::valid boolean Indicates if the tuner is in a valid state. When the tuner is of type DDC, False indicates that the DDC channel is no longer able to tune to the appropriate frequency because the CHANNELIZER it is attached to has been moved.
FRONTEND::tuner_status::available_frequency string Valid potential center frequencies for the tuner in Hz. In range(XX-YY) or csv (X,Y,Z) format.
FRONTEND::tuner_status::available_bandwidth string Valid potential bandwidth for the tuner in Hz. In range(XX-YY) or csv (X,Y,Z) format.
FRONTEND::tuner_status::available_gain string Valid potential gain for the tuner in dB. In range(XX-YY) or csv (X,Y,Z) format.
FRONTEND::tuner_status::available_sample_rate string Valid potential sample rates for the tuner. In range(XX-YY) or csv (X,Y,Z) format.
FRONTEND::tuner_status::reference_source long Indicates internal vs external reference source. 0 = internal reference; 1 = external reference.
FRONTEND::tuner_status::output_format string Indicates current output data format. Uses the SDDS digraph format.
FRONTEND::tuner_status::output_multicast string Multicast address for SDDS output. Multicast address in dotted quad notation (e.g., "192.168.0.1").
FRONTEND::tuner_status::output_vlan long vlan number for SDDS output. If there is no vlan used, indicate that with a zero.
FRONTEND::tuner_status::output_port long port number for SDDS output.
FRONTEND::tuner_status::decimation long Current decimation of tuner. Decimation values for DDC tuners. Defined as the ratio of input sample rate to output sample rate regardless of data format.
FRONTEND::tuner_status::tuner_number short Physical tuner ID. Tuner ID number within device. May represent physical tuner ordering or virtual ordering in software
FRONTEND::tuner_status::settling_time double Settling time after re-tuning in seconds

Connecting to the Device's Output

A benefit of this approach is that each tuner is single-channel, which means that there is not multi-out functionality controlled by a connectionTable. The only data outputs are out of each single-channel tuner, and each tuner supports a single stream. This means that no special name convention is needed for the connection id.

alloc_tuner_1 = frontend.createTunerAllocation(tuner_type='RDC',
                                               allocation_id='alloc_id_1',
                                               bandwidth=1000.0,
                                               returnDict=False)
coherent_request_any_feed = CF.DataType(id='FRONTEND::coherent_feeds', value=_any.to_any(['', '']))
allocation_response = agg_dev.allocate([alloc_tuner_1, coherent_request_any_feed])

# assume that component "process_narrowband" exists and has a bulkio input port that matches the rdc output
snk_1 = sb.launch('process_narrowband')
snk_2 = sb.launch('process_narrowband')

allocation_response[0].data_port.connectPort(snk_1.getPort('dataShort_in'), 'connection_id_1')
allocation_response[1].data_port.connectPort(snk_2.getPort('dataShort_in'), 'connection_id_1')

RFInfo

The RFInfo data structure has been updated to provide more information regarding the radio hardware. The updated RFInfo data structure is as follows:

name type description
rf_flow_id string unique id that describes this RF flow
rf_center_freq double center frequency for the tuned band
rf_bandwidth double bandwidth for the tuner
if_center_freq double intermediate frequency (equal to rf_center_frequency if single-stage or unknown)
spectrum_inverted boolean if true, the tuned image is the mirror image of the baseband
sensor SensorInfo detailed sensor information (e.g.: antenna, mission name)
ext_path_delays PathDelays frequency-selective delays
tdoa_sigma FreqDependentDeviations frequency-selective timing deviations
fdoa_sigma FreqDependentDeviations frequency-selective allan variance
capabilities RFCapabilities operating center frequency and bandwidth ranges
additional_info CF::Properties key/value pair describing additional information

The RFCapabilities structure has been expanded to include gain and sample rate:

module FRONTEND {
    struct RFCapabilities {
        FreqRange freq_range;
        FreqRange bw_range;
        Range sample_rate_range;
        Range gain_range;
    };
}

Connection Tracing

RFInfo, RFSource, NavData, and GPS ports inherit from the CF::UpstreamRegistrar interface. The CF::UpstreamRegistrar interface is as follows:

module CF {
    struct UpstreamTuple {
        Object upstream;
        Object port;
    };

    typedef sequence <UpstreamTuple> UpstreamSequence;

    interface UpstreamRegistrar {
        void setUpstream(in UpstreamTuple src);
        void removeUpstream(in UpstreamTuple src);
        readonly attribute UpstreamSequence upstreams;
    };
}

The CF::UpstreamRegistrar interface allows upstream connections to register themselves. As part of the port implementation, upstream ports invoke the setUpstream method when a connection is established and the removeUpstream method when the connection is removed. The upstreams readonly attribute provides a sequence of all connections to that port. Each element in the connection description includes a pointer to the upstream object (e.g.: device or component) and the upstream port instance.

Transmit CONOP

Transmit operations are performed through a transmit digital channel (TDC) device. The TDC device may or may not be the child of a TX device. If a single transmit channel is supported, then the single TDC is connected to an implied TX device. In the case where more than one TDC is attached to a single RF transmitter, then the multiple TDC devices are children of the TX device.

If the device is simple, such as a single hard-wired TDC, then the entire process space can be the TDC device. If the underlying hardware is configurable, as in the case of devices like the USRP, then a host process would scan the underlying hardware and deploy the appropriate number of TDC devices.

Transmit allocations are made as a combination of two properties: FRONTEND::tuner_allocation and FRONTEND::transmitter_allocation. The FRONTEND::tuner_allocation structure provides information regarding the center frequency, RF bandwidth, and/or sampling rate. The FRONTEND::transmitter_allocation structure provides information regarding the expected agility of the transmission:

ID name type description
FRONTEND::transmitter_allocation::min_freq min_freq double In Hz. Requested lower edge of the transmit range (-1 for ignore)
FRONTEND::transmitter_allocation::max_freq max_freq double In Hz. Requested upper edge of the transmit range (-1 for ignore)
FRONTEND::transmitter_allocation::control_limit control_limit double In seconds. control_limit >= sample_rate/(max_settle_time+min_dwell_time) is met before the next retune (-1 for ignore)
FRONTEND::transmitter_allocation::max_power max_power double In dBm. max_power => transmitted power (values less than or equal to -1000 for ignore)

As seen above, the FRONTEND::transmitter_allocation structure provides parameters for the expected agility of the transmitter; namely the minimum and maximum frequency over which transmissions may occur, the minimum effective dwell time for transmission (defined as the settling time plus the transmission time), and the maximum expected power. Each of these members may be set to "ignore" by passing a -1 as its value.

An example allocation of a transmitter is as follows:

alloc_tuner_1 = frontend.createTunerAllocation(tuner_type='TDC',
                                               allocation_id='alloc_id_1',
                                               center_frequency=1e6,
                                               returnDict=False)
alloc_tdc_1 = frontend.createTransmitAllocation(min_freq=900e3,
                                                max_freq=1.1e6,
                                                control_limit=0.1,
                                                max_power=-1,
                                                returnDict=False)
allocation_response = agg_dev.allocate([alloc_tuner_1, alloc_tdc_1])

A successful allocation returns a reference to the TDC device as well as its data port, in this case an input or "provides" port, and the control port.

Transmission Scheduling

Transmission queuing and execution is managed through BULK IO's timestamp. Providing a timestamp of 0 or in the past results in immediate transmission. Providing a timestamp in the future queues the packet for transmission when the current time matches the timestamp. This packet transmission process is shown below:

Transmission API

std::string stream_id = "testStream";
unsigned int size_packet_1 = 1000;
unsigned int size_packet_2 = 500;
unsigned int size_packet_3 = 400;
double t1 = 5;
double t2 = t1+5;
double t3 = t2+4;

BULKIO::PrecisionUTCTime tstamp = bulkio::time::utils::now();
double twsec = tstamp.twsec;

bulkio::OutShortStream outputStream = dataShort_out->getStream(stream_id);
if (!outputStream) {
    outputStream = dataShort_out->createStream(stream_id);
    outputStream.blocking(true);
}
redhawk::buffer<short> Packet1(size_packet_1);
// data would be added to Packet1 here
tstamp.twsec = twsec + t1;
outputStream.write(Packet1, tstamp);

redhawk::buffer<short> Packet2(size_packet_2);
// data would be added to Packet2 here
tstamp.twsec = twsec + t2;
outputStream.write(Packet2, tstamp);

redhawk::buffer<short> Packet3(size_packet_3);
// data would be added to Packet3 here
tstamp.twsec = twsec + t3;
outputStream.write(Packet3, tstamp);

As shown above, the different packets are queued through the port's stream API, with the provided timestamp giving the transmitter transmission directions.

Note that transmit queues are not supported in VITA49 or SDDS ports.

BULK IO is also used when transmissions are to be sent over different frequencies. The re-tuning instructions are inserted as keyword CHAN_RF in SRI updates, as seen below:

Multi-Frequency Transmission API

std::string stream_id = "testStream";
unsigned int size_packet_1 = 1000;
unsigned int size_packet_2 = 500;
double t1 = 5;
double t2 = t1+5;
double f1 = 1e6;
double f2 = 2e6;

BULKIO::PrecisionUTCTime tstamp = bulkio::time::utils::now();
double twsec = tstamp.twsec;

bulkio::OutShortStream outputStream = dataShort_out->getStream(stream_id);
if (!outputStream) {
    outputStream = dataShort_out->createStream(stream_id);
    outputStream.blocking(true);
}

redhawk::PropertyMap new_keywords;

new_keywords["CHAN_RF"] = f1;
outputStream.keywords(new_keywords);
redhawk::buffer<short> Packet1(size_packet_1);
// data would be added to Packet1 here
tstamp.twsec = twsec + t1;
outputStream.write(Packet1, tstamp);

new_keywords["CHAN_RF"] = f2;
outputStream.keywords(new_keywords);
redhawk::buffer<short> Packet2(size_packet_2);
// data would be added to Packet2 here
tstamp.twsec = twsec + t2;
outputStream.write(Packet2, tstamp);

Note that the stream id plays no role in the basic transmit API. Stream id differentiation becomes important when different transmission priorities are mixed. To provide a stream id a particular priority, add the keyword FRONTEND::PRIORITY with value of type short with the stream's priority to the SRI keywords.

std::string stream_id = "testStream";
unsigned int size_packet_1 = 1000;
unsigned int size_packet_2 = 500;
double t1 = 5;
double t2 = t1+5;
double f1 = 1e6;
double f2 = 2e6;
short priority = 3;

BULKIO::PrecisionUTCTime tstamp = bulkio::time::utils::now();
double twsec = tstamp.twsec;

bulkio::OutShortStream outputStream = dataShort_out->getStream(stream_id);
if (!outputStream) {
    outputStream = dataShort_out->createStream(stream_id);
    outputStream.blocking(true);
}

redhawk::PropertyMap new_keywords;

new_keywords["CHAN_RF"] = f1;
new_keywords["FRONTEND::PRIORITY"] = priority;
outputStream.keywords(new_keywords);
redhawk::buffer<short> Packet1(size_packet_1);
// data would be added to Packet1 here
tstamp.twsec = twsec + t1;
outputStream.write(Packet1, tstamp);

new_keywords["CHAN_RF"] = f2;
// note that new_keywords still contains FRONTEND::PRIORITY
outputStream.keywords(new_keywords);
redhawk::buffer<short> Packet2(size_packet_2);
// data would be added to Packet2 here
tstamp.twsec = twsec + t2;
outputStream.write(Packet2, tstamp);

When overlapping scheduled transmissions conflict, the transmission with the higher priority is transmitted.

Implicit Continuous burst

To transmit multiple packets as a single continuous signal burst, setting the timestamp to immediately follow end of the last symbol can lead to undefined boundary conditions.

Continuous Transmission

To remove the ambiguity of the transmitted construct, the timestamp in subsequent packet(s) can be set such that the separate packets are interpreted by the transmitter as a continuous transmitted signal. To do so, set the timestamp to invalid and both the whole (twsec) and fractional (tfsec) to 0, as shown below:

std::string stream_id = "testStream";
unsigned int size_packet_1 = 1000;
unsigned int size_packet_2 = 500;

BULKIO::PrecisionUTCTime tstamp = bulkio::time::utils::now();

bulkio::OutShortStream outputStream = dataShort_out->getStream(stream_id);
if (!outputStream) {
    outputStream = dataShort_out->createStream(stream_id);
    outputStream.blocking(true);
}

redhawk::buffer<short> Burst1(size_packet_1);
// data would be added to Burst1 here
tstamp.twsec = tstamp.twsec + 5;
outputStream.write(Burst1, tstamp);

redhawk::buffer<short> Burst2(size_packet_2);
// data would be added to Burst2 here
tstamp.tcstatus = BULKIO::TCS_INVALID;
tstamp.tfsec = 0;
tstamp.twsec = 0;
outputStream.write(Burst2, tstamp);

Explicit Continuous burst

There are instances when the concern exists that data might be lost without any feedback. This error can occur when data is received over UDP multicast rather than point-to-point Bulk IO. In such cases, a burst needs to be defined as continuous explicitly rather than implicitly. To declare a stream as continuous, used the keyword FRONTEND::CONTINUOUS_STREAM with the boolean True as its value in the SRI. Adding FRONTEND::CONTINUOUS_STREAM sets the transmitter to ignore all timestamps except for the first one.

The first timestamp provides a start cue. Subsequent timestamps are ignored. If any gaps appear on the stream, where data is not available when it is needed, a DEV_UNDERFLOW is triggered.

Transmit Arrays

Much like receivers, transmitters can be associated as arrays. The association of coherent devices is performed with the TX_ARRAY device. Much like RX_ARRAY, allocations of coherent transmit channels is by providing TX_ARRAY with the allocation property FRONTEND::coherent_feeds.

Calling allocate() on the TX_ARRAY device with FRONTEND::coherent_feeds is a sequence of the TDC devices that satisfied the request.

Feedback

Feedback from the device is sent through the TDC's status port, as seen in the following figure:

Transmitter Status

The TDC's status port is named "transmitDeviceStatus_out" and uses the TransmitDeviceStatus interface. The DeviceStatus interfaces is as follows:

module CF {
    enum DeviceStatusCodeType {
        DEV_OK,                             // device operating normally
        DEV_UNDERFLOW,                      // not enough data to fill the needed time
        DEV_OVERFLOW,                       // hardware cannot transmit the data at the rate requested
        DEV_INSUFFICIENT_SETTLING_TIME,     // time difference between end of last transmit and new transmit with a tune is insufficient
        DEV_MISSED_TRANSMIT_WINDOW,         // transmit could not begin at the requested time
        DEV_INVALID_TRANSMIT_TIME_OVERLAP,  // the timestamp for the last sample in the previous packet is after the timestamp for the first sample in the current packet
        DEV_INVALID_HARDWARE_STATE,         // hardware state is invalid (e.g.: configuration placed hardware in an invalid state)
        DEV_HARDWARE_FAILURE                // general hardware failure
    };

    // generic device status
    struct DeviceStatusType {
        string                      device_id;      // unique device instance id
        string                      allocation_id;
        CF::UTCTime                 timestamp;
        CF::DeviceStatusCodeType    status;         // device status
        string                      message;        // status-specific message
        CF::Properties              props;          // status-specific properties
    };

    interface DeviceStatus {
        // triggers for status message: change in status
        void statusChanged(in DeviceStatusType status);
    };
};

module FRONTEND {
    struct TransmitStatusType {
        string                      stream_id;
        string                      allocation_id;
        CF::UTCTime                 timestamp;
        unsigned long long          total_samples;  // number of samples transmitted from this stream id; resets to 0 when >= max Ulonglong
        unsigned long long          total_packets;  // number of packets transmitted (pushPacket calls) from this stream id; resets to 0 when >= max Ulonglong
        boolean                     transmitting;   // true when currently transmitting, false when no queued data to transmit
        CF::DeviceStatusCodeType    status;         // device status
        double                      settling_time;  // this is the hardware's current re-tune settling time
        unsigned long               queued_packets; // number of packets that are yet to be transmitted (current queue size)
    };

    interface TransmitDeviceStatus : CF::DeviceStatus {
        // triggers for status message: change in status, change in transmitting, or queued_packets increases or becomes zero
        void transmitStatusChanged(in TransmitStatusType status);
    };
};

Control of the transmitter, beyond that afforded by timestamps and keywords in BULK IO, is concerned with error recovery. For example, in some CONOPs it may be required to have a well-defined strict schedule of transmitted packets, while in other CONOPs, it may be acceptable for some packets to be sent with loose timing or not at all. The response to error states is defined by the TransmitControl interface, which in turn inherits from the DigitalTuner interface:

interface TransmitControl : DigitalTuner {
    struct TransmitParameters {
        string  stream_id;          // if empty string, then applies to all streams
        boolean ignore_timestamp;   // set to true for continuous data. Insufficient data will cause an DEV_UNDERFLOW error
        boolean ignore_error;       // set to true to ignore error states and just transmit
        double  tx_power;           // -1 for don't care
        double  max_timing_error;   // how much deviation (over/under) before DEV_MISSED_TRANSMIT_WINDOW is set as the error code; -1 for don't care
        // time for the packet transmission is the bulk io timestamp, unless ignore_timestamp is true; then data is sent as soon as it is available
        // center frequency for the packet transmission is CHAN_RF keyword in SRI
        // duration of the packet is the length of the packet * the sampling period (in SRI)
    };

    // reset allocation(s):
    //    - remove error codes
    //    - empty the transmit queue
    //    - reset total_samples count
    //    - reset total_packets count
    // if stream_id == "", the reset applies to all streams
    void reset(in string stream_id)
        raises (FRONTEND::FrontendException);

    // do not transmit the given stream id
    // queue the transmission until the stream id is allowed again
    //  return false if the stream id does not exist
    //  return true if the stream id exists
    boolean hold(in string stream_id)
        raises (FRONTEND::FrontendException);

    // list the stream ids that are currently in a "hold" state
    CF::StringSequence held()
        raises (FRONTEND::FrontendException);

    // allow the given stream id to transmit
    // if the transmission is queued, immediately transmit if timestamp is zero or set to ignore
    //  return false if the stream id does not exist
    //  return true if the stream id exists
    boolean allow(in string stream_id)
        raises (FRONTEND::FrontendException);

    void setTransmitParameters(in TransmitParameters transmit_parameters)
        raises (FRONTEND::FrontendException, FRONTEND::BadParameterException);

    TransmitParameters getTransmitParameters()
        raises (FRONTEND::FrontendException);
};

Note that the allocation id is not an argument for these function calls. Since each TDC is associated with a single allocation, a call over the TDC's TransmitControl interface is implicitly associated with the allocation. Calls to a device over the TransmitControl interface apply to all children of the devices that support the TransmitControl interface.

The reset method of the TransmitControl interface allows a device to exit the error state, empty its transmit queue, and reset all transmission counters. Set ignore_error to true in the TransmitParameters for the device's best-effort attempt to transmit data irrespective of error state. If ignore_error is set to true and an error condition occurs, such as an DEV_OVERFLOW, the device will notify the error state but continue to transmit.

Quick Response

In cases where a response to a particular event is required quickly, it may not be feasible to build the transmission (multiple pushPacket and pushSRI) in the time allowed. For these cases, the transmit API allows the creation of a response and queue it in the device. When the time to transmit is identified, a single call can be used to trigger the event.

To build a transmit queue, use the hold() method to hold a particular stream id. The return value of the hold method is a boolean describing whether or not the transmitter is aware that the stream id exists. A return value of false means that the device has yet to see the stream id. Irrespective of the return value, any arriving data associated with this stream id is queued.

To trigger a transmission, call allow() with the argument set to the selected stream id. The transmit queue for this stream is emptied as described in the following list:

To retrieve the list of streams that are on hold, call the held() method.

Error States

There are multiple errors associated with the FEI transmit API.

DEV_UNDERFLOW

The transmitter sets its transmission rate based on the xdelta (sampling period) for the stream. DEV_UNDERFLOW occurs when ignore_timestamp is set to true and there is not enough queued data to maintain the requested transmit rate.

DEV_OVERFLOW

The given xdelta (sampling period) for the stream indicates a sample rate that is higher than the maximum rate supported by the transmitter. If ignore_error is true, data is transmitted at the maximum rate supported by the hardware.

DEV_INSUFFICIENT_SETTLING_TIME

The time difference between packets where a re-tune occurred is insufficient for the transmitter to settle to the new frequency before the provided timestamp expires. This error cannot occur if ignore_timestamp is set to true.

DEV_MISSED_TRANSMIT_WINDOW

The data packet was received too late to honor the timestamp. This error cannot occur if ignore_timestamp is true.

DEV_INVALID_TRANSMIT_TIME_OVERLAP

The difference in timestamp between consecutive packets is less than the length of the first packet. This error cannot occur if ignore_timestamp is true.

DEV_INVALID_HARDWARE_STATE

The hardware has been incorrectly configured. The hardware cannot function as configured and the device is no longer transmitting irrespective of the state of the ignore_error flag.

DEV_HARDWARE_FAILURE

A catastrophic hardware failure has been detected. The hardware can no longer function and the device is no longer transmitting irrespective of the state of the ignore_error flag.

Conflicting transmit stream scenarios

The following sections describe several scenarios (and the resulting behavior) where the timestamp and priority between different streams conflict.

Transmission with Multiple Priorities and Not Ignore Errors

Transmission with mixed priorities will lead to mixed error conditions. Suppose the transmitter is currently supporting transmissions for "Stream A" with no set priority. The transmissions for "Stream A" are queued and are in the process of transmission. A new stream, "Stream B", is received by the TDC. "Stream B" has FRONTEND::PRIORITY = 5, and transmissions that overlap with "Stream A" packets. Assuming that stream_id = "", ignore_error = false, and ignore_timestamp = false in TransmitParameters, the following occurs:

Transmission with Multiple Priorities and Ignore Errors

Transmission with mixed priorities will lead to mixed error conditions. Suppose the transmitter is currently supporting transmissions for "Stream A" with no set priority. The transmissions for "Stream A" are queued and are in the process of transmission. A new stream, "Stream B", is received by the TDC. "Stream B" has FRONTEND::PRIORITY = 5, and has transmissions that overlap with "Stream A" packets. Assuming that stream_id = "", ignore_error = true, and ignore_timestamp = false in TransmitParameters, the following occurs:

Transmission with Multiple Priorities and Ignore Timestamp (both values of Ignore Errors)

Transmission with mixed priorities will lead to mixed error conditions. Suppose the transmitter is currently supporting transmissions for "Stream A" with no set priority. The transmissions for "Stream A" are queued and are in the process of transmission. A new stream, "Stream B", is received by the TDC. "Stream B" has FRONTEND::PRIORITY = 5, and has transmissions that overlap with "Stream A" packets. Assuming that stream_id = "", ignore_error = false, and ignore_timestamp = true in TransmitParameters, the following occurs:

Because no error condition was triggered, the value of ignore_error has no effect on this scenario.

Transmission with Equal (or no) Priorities and Not Ignore Errors

Transmission with equal priorities or priorities not set will lead to consistent error conditions. Suppose the transmitter is currently supporting transmissions for "Stream A" with no set priority. The transmissions for "Stream A" are queued and are in the process of transmission. A new stream, "Stream B", is received by the TDC also with no set priority. "Stream B" has transmissions that overlap with "Stream A" packets. Assuming that stream_id = "", ignore_error = false, and ignore_timestamp = false in TransmitParameters, the following occurs:

Transmission with Equal (or no) Priorities and Ignore Errors

Transmission with equal priorities or priorities not set will lead to consistent error conditions. Suppose the transmitter is currently supporting transmissions for "Stream A" with no set priority. The transmissions for "Stream A" are queued and are in the process of transmission. A new stream, "Stream B", is received by the TDC also with no set priority. "Stream B" has transmissions that overlap with "Stream A" packets. Assuming that stream_id = "", ignore_error = true, and ignore_timestamp = false in TransmitParameters, the following occurs:

Error Recovery

To remove the error state from any one stream, reset() must be invoked on the TDC's control port. After reset() is invoked, any stream in an error state (e.g.: "Stream A" in the above examples), has the following event issued: