ektologo

Properly handle asynchronous UHD overflow messages

by [fito]

When using the usrp_source block in GNUradio, you have likely experienced overflows. Overflows occur when the host does not consume data quickly enough.

## Problem

Whenever an overflow occurs, the usrp_source block provides an error message that looks like this:

usrp_source :error: In the last 778 ms, 1 overflows occurred.

Running the following flowgraph (test_overflow_msg.py) is an easy way to reproduce this situation:

#!eusr/bin/env python3
from gnuradio import blocks, gr, uhd

class OverflowTest(gr.top_block):
    def __init__(self):
        gr.top_block.__init__(self, self.__class__.__name__, catch_exceptions=True)
        self.sample_rate = 1e6

        # Blocks
        self.uhd_source = uhd.usrp_source("", uhd.stream_args(cpu_format="fc32", channels=range(1)))
        self.uhd_source.set_samp_rate(self.sample_rate)
        self.throttle = blocks.throttle(gr.sizeof_gr_complex*1, self.sample_rate / 10, True)
        self.null_sink = blocks.null_sink(gr.sizeof_gr_complex*1)

        # Connections
        self.connect(self.uhd_source, self.throttle, self.null_sink)

if __name__ == '__main__':
    tb = OverflowTest()
    tb.start()
    try:
        input('Press Enter to quit: ')
    except EOFError:
        pass
    tb.stop()
    tb.wait()

Here is the output:

# python /tmp/test_overflow_msg.py
[INFO] [UHD] linux; GNU C++ version 12.2.0; Boost_108000; UHD_4.3.0.HEAD-0-g1f8fd345
[INFO] [B200] Detected Device: B200
[INFO] [B200] Operating over USB 3.
[INFO] [B200] Initialize CODEC control...
[INFO] [B200] Initialize Radio control...
[INFO] [B200] Performing register loopback test...
[INFO] [B200] Register loopback test passed
[INFO] [B200] Setting master clock rate selection to 'automatic'.
[INFO] [B200] Asking for clock rate 16.000000 MHz...
[INFO] [B200] Actually got clock rate 16.000000 MHz.
[INFO] [B200] Asking for clock rate 32.000000 MHz...
[INFO] [B200] Actually got clock rate 32.000000 MHz.
Press Enter to quit: Ousrp_source :error: In the last 1174 ms, 1 overflows occurred.
Ousrp_source :error: In the last 778 ms, 1 overflows occurred.
Ousrp_source :error: In the last 778 ms, 1 overflows occurred.
OOusrp_source :error: In the last 1515 ms, 2 overflows occurred.
Ousrp_source :error: In the last 778 ms, 1 overflows occurred.
OOusrp_source :error: In the last 1515 ms, 2 overflows occurred.
Ousrp_source :error: In the last 778 ms, 1 overflows occurred.
OOusrp_source :error: In the last 1515 ms, 2 overflows occurred.

The problem arises when you want to handle overflows within your code; there's no way to do so.

## Proposal

To address this problem, the best approach is to modify gr-uhd/lib/usrp_source_impl.cc in GNU Radio.

The idea is to:

As of 25/10/2022, the diff would be:

❯ git diff
diff --git a/gr-uhd/grc/gen_uhd_usrp_blocks.py b/gr-uhd/grc/gen_uhd_usrp_blocks.py
index 371fe937c..42c28946e 100644
--- a/gr-uhd/grc/gen_uhd_usrp_blocks.py
+++ b/gr-uhd/grc/gen_uhd_usrp_blocks.py
@@ -119,10 +119,10 @@ outputs:
 % if sourk == 'sink':

 outputs:
+% endif
 -   domain: message
     id: async_msgs
     optional: true
-% endif

 templates:
     imports: |-
diff --git a/gr-uhd/lib/usrp_source_impl.cc b/gr-uhd/lib/usrp_source_impl.cc
index 510ab371c..c942076de 100644
--- a/gr-uhd/lib/usrp_source_impl.cc
+++ b/gr-uhd/lib/usrp_source_impl.cc
@@ -49,6 +49,7 @@ usrp_source_impl::usrp_source_impl(const ::uhd::device_addr_t& device_addr,

     _samp_rate = this->get_samp_rate();
     _samps_per_packet = 1;
+    message_port_register_out(ASYNC_MSGS_PORT_KEY);
     register_msg_cmd_handler(cmd_tag_key(),
                              [this](const pmt::pmt_t& tag, const int, const pmt::pmt_t&) {
                                  this->_cmd_handler_tag(tag);
@@ -647,6 +648,12 @@ int usrp_source_impl::work(int noutput_items,
                 std::chrono::duration_cast<std::chrono::milliseconds>(delta).count();
             d_logger->error(
                 "In the last {:d} ms, {:d} overflows occurred.", ms, _overflow_count);
+
+            pmt::pmt_t value = pmt::dict_add(
+                pmt::make_dict(), EVENT_CODE_OVERFLOW, pmt::from_uint64(_overflow_count));
+            pmt::pmt_t msg = pmt::cons(ASYNC_MSG_KEY, value);
+            message_port_pub(ASYNC_MSGS_PORT_KEY, msg);
+
             _overflow_count = 0;
         }
         // ignore overflows and try work again
diff --git a/gr-uhd/lib/usrp_source_impl.h b/gr-uhd/lib/usrp_source_impl.h
index 437fe7915..8cd986eb8 100644
--- a/gr-uhd/lib/usrp_source_impl.h

## Result

In order to test the proposal we just need to compile and modify a little bit test_overflow_msg.py:

❯ diff -c3 test_overflow_msg.py test_overflow_msg_new.py
*** test_overflow_msg.py
--- test_overflow_msg_new.py
***************
*** 15,23 ****
--- 15,25 ----
          self.uhd_source.set_samp_rate(self.sample_rate)
          self.throttle = blocks.throttle(gr.sizeof_gr_complex*1, self.sample_rate / 10, True)
          self.null_sink = blocks.null_sink(gr.sizeof_gr_complex*1)
+         self.print_msg = blocks.message_debug()

          # Connections
          self.connect(self.uhd_source, self.throttle, self.null_sink)
+         self.msg_connect((self.uhd_source, 'async_msgs'), (self.print_msg, 'print'))

  if __name__ == '__main__':
      tb = OverflowTest()

And the output is:

# python3 /tmp/test_overflow_msg_new.py
[INFO] [UHD] linux; GNU C++ version 13.2.1 20230801; Boost_108300; UHD_4.5.0.HEAD-0-g471af98f
[INFO] [B200] Detected Device: B200
[INFO] [B200] Operating over USB 3.
[INFO] [B200] Initialize CODEC control...
[INFO] [B200] Initialize Radio control...
[INFO] [B200] Performing register loopback test...
[INFO] [B200] Register loopback test passed
[INFO] [B200] Setting master clock rate selection to 'automatic'.
[INFO] [B200] Asking for clock rate 16.000000 MHz...
[INFO] [B200] Actually got clock rate 16.000000 MHz.
[INFO] [B200] Asking for clock rate 32.000000 MHz...
[INFO] [B200] Actually got clock rate 32.000000 MHz.
Press Enter to quit: Ousrp_source :error: In the last 1110 ms, 1 overflows occurred.
******* MESSAGE DEBUG PRINT ********
(uhd_async_msg (overflows . 1))
************************************
Ousrp_source :error: In the last 778 ms, 1 overflows occurred.
******* MESSAGE DEBUG PRINT ********
(uhd_async_msg (overflows . 1))
************************************
Ousrp_source :error: In the last 778 ms, 1 overflows occurred.
******* MESSAGE DEBUG PRINT ********
(uhd_async_msg (overflows . 1))
************************************
OOusrp_source :error: In the last 1515 ms, 2 overflows occurred.
******* MESSAGE DEBUG PRINT ********
(uhd_async_msg (overflows . 2))
************************************
Ousrp_source :error: In the last 778 ms, 1 overflows occurred.
******* MESSAGE DEBUG PRINT ********
(uhd_async_msg (overflows . 1))
************************************

## Update (3/1/2023)

We proposed the change to be added into GNU Radio and it's already merged since 3.10.5.1.