[Python] Creating a GNU Radio block that executes before the main flowgraph is started

Discussão em 'Python' iniciado por Stack, Maio 7, 2025.

  1. Stack

    Stack Membro Participativo

    I created a GNU Radio block called "Doppler File Generator" following the Python OOT tutorial and the previous question I asked back in April. This block generates a .txt file containing two columns: timestamp and Doppler frequency correction. The file is intended to be read by another block—Doppler Correction—which applies the necessary frequency shift correction to the signal. This Doppler Correction block is part of the main flowgraph.

    The flowgraph structure is as follows: Osmocom Source → Doppler Correction Block (reads the generated .txt file) → Frequency Xlating FIR Filter → QT GUI Sink

    Ideally, when the flowgraph is executed, the Doppler File Generator block (my block) should run first to produce the .txt file, after which the rest of the flowgraph processes the signal using that file. However, this is not happening. Instead, GNU Radio executes the main flowgraph first, before the .txt file is generated. As a result, the Doppler Correction block throws an error stating that it cannot find the .txt file because the file was not generated.

    In the YAML configuration file, I set the init_before_blocks parameter as shown in the Python Snippet YAML example, but this still doesn't force the Doppler File Generator block to execute first.

    I tested the Doppler File Generator block independently in a separate flowgraph and it works correctly. So, I believe the issue lies in the flowgraph hierarchy or execution order.

    Below are my codes:

    YAML:

    id: customModule_DopplerFileGenerator
    label: Doppler File Generator
    category: '[customModule]'
    flags: [python]

    templates:
    imports: from gnuradio.customModule import DopplerFileGenerator
    make: DopplerFileGenerator(
    ${tle_line1},
    ${tle_line2},
    ${frequency},
    ${timestamp},
    ${output_file_path})
    callbacks:
    - open(${output_file_path})

    parameters:
    - id: section
    label: Section of Flowgraph
    dtype: string
    options: ['main_after_init', 'main_after_start', 'main_after_stop', 'init_before_blocks']
    option_labels: ['Main - After Init', 'Main - After Start', 'Main - After Stop', 'Init - Before Blocks']
    default: 'init_before_blocks'

    - id: priority
    label: Priority
    dtype: int
    default: 0
    hide: ${'part' if priority <= 0 else 'none'}

    - id: tle_line1
    label: TLE Line 1
    dtype: string
    default: ''

    - id: tle_line2
    label: TLE Line 2
    dtype: string
    default: ''

    - id: frequency
    label: Frequency (Hz)
    dtype: float
    default: 0

    - id: timestamp
    label: Timestamp (Unix Epoch)
    dtype: float
    default: 0

    - id: output_file_path
    label: Output File Path
    dtype: file_save

    inputs: []
    outputs: []
    file_format: 1


    The python code which generates the .txt file:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    #
    # SPDX-License-Identifier: GPL-3.0-or-later

    import scipy.constants
    import skyfield.api
    from skyfield.api import wgs84, EarthSatellite
    import numpy as np
    import datetime
    import os


    class DopplerFileGenerator:
    def __init__(self, tle_line1='', tle_line2='', frequency=0.0, timestamp=0.0, output_file_path=''):


    print("[DopplerFileGenerator] Initializing and generating Doppler file...")

    self.tle_line1 = tle_line1
    self.tle_line2 = tle_line2
    self.frequency = frequency
    self.timestamp = timestamp
    self.output_file_path = output_file_path
    self.compute_doppler()

    def compute_doppler(self):
    try:
    ts = skyfield.api.load.timescale()
    groundstation = wgs84.latlon(53.1111, 8.8583, 0)

    satellite = EarthSatellite(self.tle_line1, self.tle_line2, 'satellite', ts)
    unix_epoch = datetime.datetime(1970, 1, 1, tzinfo=skyfield.api.utc)

    t0 = unix_epoch + datetime.timedelta(seconds=float(self.timestamp))
    t0 = ts.from_datetime(t0.replace(tzinfo=skyfield.api.utc))

    DAY_S = 24 * 3600
    t = t0 + np.arange(0, (25 * 60 + 0.1) / DAY_S, 0.1 / DAY_S)
    t = ts.tai_jd([s.tai for s in t])

    topocentric = (satellite - groundstation).at(t)
    range_rate = topocentric.frame_latlon_and_rates(groundstation)[5].km_per_s * 1e3
    doppler = -range_rate / scipy.constants.c * float(self.frequency)

    os.makedirs(os.path.dirname(self.output_file_path), exist_ok=True)
    with open(self.output_file_path, 'w') as output_file:
    for s, f in zip(t, doppler):
    s = (s.utc_datetime() - unix_epoch).total_seconds()
    output_file.write(f'{s:.3f}\t{f:.3f}\n')

    print(f"[DopplerFileGenerator] Output saved to {self.output_file_path}")
    except Exception as e:
    print(f"[DopplerFileGenerator] Error: {e}")


    So far, I have tried the following:


    1. Modified the YAML file to ensure my block executes first by setting the init_before_blocks parameter, similar to the Python Snippet block.


    2. Deleted the GNU Radio subclass from my Python code.


    3. Tried placing raw code in the python/customModule/init.py file. This works partially, but I have to enter the data manually in the code instead of configuring it through GNU Radio.

    Any suggestions? It works with Python Snipped block but I had to use tkinter to make the interface. I want to make it as OOT Block so it can be installed as part of GNU Radio. I am experimenting.

    [​IMG]

    Continue reading...

Compartilhe esta Página