2

I am very new to GNU Radio, and I am stumbling to realise a periodic frequency band sweeper.

The idea is as follow: given a sampling rate samp_rate, a start frequency f_start, a stop frequency f_stop, a number of channels n_channels, and a sweep rate sweep_rate, I would like to go from f_start to f_stop periodically at a rate given by sweep_rate (meaning having sweep_rate repetitions f_start -> ... -> f_stop -> f_start -> ... every second).

So far, I managed to have a periodic repetition with the embedded python code here below

"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds to get ports and parameters of your block. The arguments to init will be the parameters. All of them are required to have default values! """

import numpy as np from gnuradio import gr import pmt

class blk(gr.sync_block): # other base classes are basic_block, decim_block, interp_block """! Embedded Python Block Function changing periodically the signal source's frequency, from f_min to f_max, by step of BW_per_channel [i.e. (f_max-f_min)/(n_channels-1)]

Input:
------
n_channels: <class int> (default: 3)
    Number of frequencies (called channels)

samp_rate: <class float> (default: 32e3)
    Number of samples to have 1 second

sweep_rate: <classs int> (default: 1)
    Number of total bandwidth sweepings per second

f_start: <class float> (default: 2.4e3)
    First channel

f_stop: <class float> (default: 2.5e3)
    Last channel
"""

def __init__(self, n_channels=3, samp_rate=32e3, sweep_rate=1, f_start=2.4e3, f_stop=2.5e3): # only default arguments here
    """arguments to this function show up as parameters in GRC"""
    gr.sync_block.__init__(
        self,
        name='Frequency sweeper', # will show up in GRC
        in_sig=[np.float32],
        out_sig=[]
    )
    # if an attribute with the same name as a parameter is found,
    # a callback is registered (properties work, too).
    self.n_channels = n_channels
    self.samp_rate = samp_rate
    self.sweep_rate = sweep_rate
    self.f_start = f_start
    self.f_stop = f_stop

    self.BW_tot = self.f_stop - f_start

    self.frequencies = [self.f_start + i*self.BW_tot/(self.n_channels-1) for i in range(self.n_channels)]

    self.counter = 0 # if bigger than given threshold, update the frequency
    self.i = 0 # which frequency to select

    self.t_per_freq = self.samp_rate/self.sweep_rate/self.n_channels

    self.outPortFreq = "frequency"
    self.message_port_register_out(pmt.intern(self.outPortFreq))
    self.outPortDebugMsg = "debug"
    self.message_port_register_out(pmt.intern(self.outPortDebugMsg))



def work(self, input_items, output_items):
    """!
    Example: change frequency of signal source periodically
    """

    # Create dictionaries to send messages to source and terminal
    dict_debug = pmt.make_dict()
    dict_msg = pmt.make_dict()

    # Some debug messages sent to the terminal
    dict_debug = pmt.dict_add(dict_debug, pmt.intern("t_per_freq"), pmt.from_double(self.t_per_freq))
    dict_debug = pmt.dict_add(dict_debug, pmt.intern("counter"), pmt.from_double(self.counter))
    pmt_msg_debug = pmt.dict_add(dict_debug, pmt.intern(f"freq[{self.i}]"), pmt.from_double(self.frequencies[self.i]))
    self.message_port_pub(pmt.intern(self.outPortDebugMsg), pmt_msg_debug)

    self.counter = self.counter + len(input_items[0])

    if self.counter > self.t_per_freq:
        self.i += 1
        self.counter = 0

    if self.i > len(self.frequencies)-1: self.i = 0

    # Message sent to update the source frequency
    pmt_msg = pmt.dict_add(dict_msg, pmt.intern("freq"), pmt.from_double(self.frequencies[self.i]))
    self.message_port_pub(pmt.intern(self.outPortFreq), pmt_msg)

    return len(input_items[0])

but the sweep frequency is not correct, and I do not know why. As an example, with n_channels = 50, sweep_rate = 1, samp_rate = 100e3, f_start = 2.4e3, and f_stop = 2.5e3, I have the result shown in the picture.enter image description here which is obviously not the result I was hoping for. Can someone help me out ? I also attach to this post the complete .grc, the complete custom embedded Python block, and the complete Python file for better help.


Update:

I managed to have a real time modification of sweep_rate, but the period is still not right. Does anyone have an idea of why ? Here below is the updated code of my embedded Pyth block

"""
Embedded Python Blocks:

Each time this file is saved, GRC will instantiate the first class it finds to get ports and parameters of your block. The arguments to init will be the parameters. All of them are required to have default values! """

import numpy as np from gnuradio import gr from gnuradio.gr import log import pmt, math

class blk(gr.sync_block): # other base classes are basic_block, decim_block, interp_block """! Embedded Python Block Function changing periodically the signal source's frequency, from f_min to f_max, by step of BW_per_channel [i.e. (f_max-f_min)/(n_channels-1)]

Input:
------
n_channels: <class int> (default: 3)
    Number of frequencies (called channels)

samp_rate: <class float> (default: 32e3)
    Number of samples to have 1 second

sweep_rate: <classs int> (default: 1)
    Number of total bandwidth sweepings per second

f_start: <class float> (default: 2.4e3)
    First channel

f_stop: <class float> (default: 2.5e3)
    Last channel
"""

def __init__(self, n_channels=3, samp_rate=32e3, sweep_rate=1, f_start=2.4e3, f_stop=2.5e3): # only default arguments here
    """arguments to this function show up as parameters in GRC"""
    gr.sync_block.__init__(
        self,
        name='Frequency sweeper', # will show up in GRC
        in_sig=[np.float32],
        out_sig=[]
    )
    self.n_channels = n_channels
    self.samp_rate = samp_rate
    self.sweep_rate = sweep_rate
    self.f_start = f_start
    self.f_stop = f_stop

    self.BW_tot = self.f_stop - f_start

    self.frequencies = [self.f_start + i*self.BW_tot/(self.n_channels-1) for i in range(self.n_channels)]

    self.counter = self.nitems_written(0) # if bigger than given threshold, update the frequency
    self.i = 0 # which frequency to select

    self.t_per_freq = self.samp_rate/self.sweep_rate/self.n_channels

    self.outPortFreq = "frequency"
    self.message_port_register_out(pmt.intern(self.outPortFreq))



def work(self, input_items, output_items):
    """!
    Example: change frequency of signal source periodically
    """

    # Create dictionary to send messages to source
    dict_msg = pmt.make_dict()

    # Some messages sent to the terminal
    log.info( f"\r \
                \rt_per_freq={self.t_per_freq} \
                \rcounter={self.counter} \
                \rfreq[{self.i}]={self.frequencies[self.i]} \
                \rsweep_rate={self.sweep_rate} \
                \rlen(input_items[0])={len(input_items[0])}" )

    self.t_per_freq = self.samp_rate / self.sweep_rate / self.n_channels
    self.counter += len(input_items[0])

    if self.counter > self.t_per_freq:
        self.i += 1
        self.counter = 0

    if self.i > len(self.frequencies)-1: self.i = 0

    # Message sent to update the source frequency
    pmt_msg = pmt.dict_add(dict_msg, pmt.intern("freq"), pmt.from_double(self.frequencies[self.i]))
    self.message_port_pub(pmt.intern(self.outPortFreq), pmt_msg)

    return len(input_items[0])

David Hoelzer
  • 1,043
  • 1
  • 9
  • 31
aheuchamps
  • 21
  • 1
  • 6
  • 2
    instead of a self.counter you could use self.nitems_written(0): GNU Radio already has to count the items you produced :) – Marcus Müller Feb 14 '22 at 17:23
  • 1
    instead of sending debug messages, you could use the GNU Radio logger! from gnuradio.gr import log, then in your work you could do log.debug("huhu") (or log.trace, or log.info or log.warn) – Marcus Müller Feb 14 '22 at 17:23
  • @MarcusMüller Thank you for your answer and suggestions. I made the changes you proposed, but still the sweeping frequency is not correct (I used the same parameter set as previously). Any idea on how to correct that ? Alex – aheuchamps Feb 15 '22 at 11:48
  • 1
    Not sure if you have seen this, but a similar question was asked over on the ADI forums: https://ez.analog.com/wide-band-rf-transceivers/design-support/f/q-a/107548/fast-frequency-sweep-with-the-pluto-in-gnu-radio There is a part about one of the sinks you can use in a particular way. –  Feb 15 '22 at 13:11
  • @jdv Thank you for the pointer, I will check it. Have a nice day Alex – aheuchamps Feb 15 '22 at 13:22
  • 1
    @aheuchamps if that link helps, make sure you come back and self-answer! I am interested in this situation as well. –  Feb 16 '22 at 15:42
  • Hey @jdv I have something in which I can change the value of sweep_rate dynamically and effectively changes the sweeping, but the math to get the right sweeping frequency is still not correct. To do so, I just had to put the computation inside the work() function of the Python block, i.e. put self.t_per_freq = self.samp_rate / self.sweep_rate / self.n_channels inside it. Any idea on how to compute the value of t_per_freq ? Have a nice day, Alex. – aheuchamps Feb 17 '22 at 16:10
  • 1
    @aheuchamps that sounds like a new question. You might want to create an Answer here even if it is a partial one and see if others can add to it. –  Feb 17 '22 at 17:56
  • 1
    Hello and welcome to ham.stackexchange.com! – rclocher3 Feb 23 '22 at 00:38
  • 1
    @rclocher3 Hello, thank you for your greetings. Have a nice day, Alex – aheuchamps Feb 26 '22 at 09:32

0 Answers0