Search code examples
c++zeromqgnuradiognuradio-companion

Sending data from CPP code to GNU Radio Companion via ZMQ


I have the following piece of code called zmq_send.cpp that sends data to a flowgraph in GNU Radio Companion via ZMQ.

#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <iostream>
using namespace std;

int main (void)
{
    void *context = zmq_ctx_new ();
    void *requester = zmq_socket (context, ZMQ_PUSH);
    zmq_connect (requester, "tcp://192.168.0.243:2000");

    unsigned short buffer[1];
    buffer[0] = 1;

    while(1)
    {
        cout << "Sending " << buffer[0] << endl;
        zmq_send (requester, buffer, 2*1, 0);
        buffer[0] = buffer[0] + 1;
        sleep(1);
    }
    zmq_close (requester);
    zmq_ctx_destroy (context);
    return 0;
}

I wrote another code called zmq_recv.cpp that recieved data from the previous code:

#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <iostream>
using namespace std;

int main (void)
{
    void *context = zmq_ctx_new ();
    void *responder = zmq_socket (context, ZMQ_PULL);
    int rc = zmq_bind (responder, "tcp://192.168.0.243:2000");
    assert (rc == 0);

    unsigned short buffer [1];

    while (1) {
        zmq_recv (responder, buffer, 2*1, 0);
        cout << buffer[0];
        cout << endl;
        sleep (1);
    }
    return 0;
}

When I send data between these codes, the data is sent successfully from the sender to the receiver. However, if I try to send data from zmq_send.cpp to GNU Radio Companion, the data is not received in the grc file. No data is seen in the time sink and no data is saved in the file. enter image description here

Can anyone please explain to me why gnuradio is not receiving data from the sender?


Solution

  • TL;DR

    The GNU Radio ZMQ blocks follow the paradigm that sinks bind and sources connect.

    Because your source code calls connect and the ZMQ PULL Source also calls connect, there is no bind call to create the socket.

    Change your code to bind and you should see data flow.

    The Gory Details

    GNU Radio uses a common underlying implementation for all ZMQ blocks. The message protocol depends on what options you set, which can be confusing, and needs to be carefully aligned on both sides. This is not related to your issue, but is worth noting.

    Multi-part Messages

    For PUB/SUB, the source/sink blocks allow for a filter key to be specified. If present, the payload is sent as a multi-part message. Because ZMQ handles the filtering, the first part is discarded, since it is of no further value.

    If no filter key is specified (e.g., it's value is '' or the block is not PUB/SUB), then multi-part messages are not used.

    Tags

    If Pass Tags is enabled, then the payload buffer is prepended with a header block, in the native byte order of the sender, which consists of the following elements:

    uint16_t  header_magic;   // 0x5ff0
    uint8_t   header_version; // 0x01 as of 3.10
    uint64_t  offset;         // starting stream position
    uint64_t  ntags;          // the number of tags sent
    

    These elements are packed with no padding. For more details, consult the source.

    Following this header, each tag is passed as a 4-tuple:

    uint64_t  tag_offset;    // offset for this tag
    char[]    key;           // encoded using pmt::serialize ... 
    char[]    value;         // decode using pmt::deserialize
    char[]    srcid;         //
    

    The PMT serialization is portable between architectures, but the integer data elements are not, so don't use GNU Radio ZMQ blocks if you are mixing big-endian and little-endian architectures.

    After all the tags have been appended, this header buffer is inserted in the front of the payload message.

    Data

    Finally, the data stream is appended to the payload message. This is a simple byte-copy in host-order of however much data was provided. This could be any number of data items. Sending one short at a time might not give you the best performance. If the source is a GNU Radio block, the number of items is determined by the scheduler and will vary depending on several factors.

    Summary

    ZMQ requires that there be exactly one bind to an endpoint, but allows any number of connects. GNU Radio uses bind for sinks, and connect for sources. Your code is backwards in this regard.

    ZMQ does not enforce any particular serialization, but if the sender and receiver don't agree, then bad things can be expected.

    GNU Radio uses a variable-format serialization, meaning the structure of the message sent changes based on the settings you use. The simplest case is just a stream of data elements. Your flowgraph meets this criteria. However, be aware of where the dragons lie.