Home » Radio » Software Defined Radio » GNURadio Envelope Detector in Python

GNURadio Envelope Detector in Python

gnuradio envelope detector

Here is a simple demonstration of using embedded Python to write a GNURadio AM Envelope Detector and explore its performance with various signals. 

Previously, I have described how the AM envelope detector works and talked about its limitations. Cheap and cheerful, yes. High performance, no. Envelope detectors are comprised of a diode – which rectifies an AM signal – and a low-pass filter to eliminate the carrier and leave the audio. A simple AM detector can be made from a diode, and a correctly selected resister and capacitor. The RC filter causes an exponential decay and filters higher frequencies.

So, I thought I would build a GNURadio Embedded Python Block to implement envelope detection. Here is a video of the experiment. You can see in the graphic above that the model eliminated most but not all of the carrier. That’s asking too much of a single pole RC filter, and additional filtering is required.

By the way, this GNURadio Envelope Detector does not work very well with IQ data derived from a software defined radio. Typically, with cheaper SDR, IQ data will contain a DC offset, and this DC offset is not stationary. As a result, the signal is not pure AC and using a rectifier does not work. For this reason, unless your IQ data is really cleaned up, you need to use other detectors for AM. For example, the built in GNURadio AM Demodulator works with the Magnitude signal from the complex data.

GNURadio Envelope Detector – Python Source Code

Here is the source code for the GNURadio Envelope Detector I implemented in an Embedded Python Block.

import numpy as np
from gnuradio import gr


class blk(gr.sync_block):  # other base classes are basic_block, decim_block, interp_block
    """Embedded Python Block Envelope Detector """

    def __init__(self, threshold=0.0, mode=0, coeff=0.15):  # only default arguments here
        """arguments to this function show up as parameters in GRC"""
        gr.sync_block.__init__(
            self,
            name='Envelope Detector',   # will show up in GRC
            in_sig=[np.float32],
            out_sig=[np.float32]
        )
        # if an attribute with the same name as a parameter is found,
        # a callback is registered (properties work, too).

        self.ry = 0
        self.threshold = threshold
        self.mode = mode
        self.coeff = coeff

    def work(self, input_items, output_items):
        """Envelope Detect with Half/Full Wave Rectifier"""
        buf = [0] * len(output_items[0])
        a0 = self.coeff
        b1 = 1 - a0
        # Rectify the signal. Mode 0 is half-wave, Mode 1 is full-wave
        for i in range (0, len(input_items[0])) :
            if self.mode == 1:
                buf[i] = abs(input_items[0][i])
            else:
                if input_items[0][i] > self.threshold:
                    buf[i] = input_items[0][i]
                else:
                    buf[i] = 0
        # Simulate simple RC filter with a variable decay coefficient
        for i in range(0, len(output_items[0])):
            if i==0:
                output_items[0][i] = a0*buf[i] + b1*self.ry
            else:
                output_items[0][i] = a0*buf[i] + b1*output_items[0][i-1]

        i = len(output_items[0])-1
        self.ry = output_items[0][i]

        return len(output_items[0])

You can download the complete project here. 

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.