mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
examples: tx_waveforms: improved comments and help
Improving user experience with examples. - added example's functionality and intent in --help - added each arg help description in --help - added summary to each function module - added more prints in during operation
This commit is contained in:
parent
2725c664ce
commit
7615602b7c
@ -1,10 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
# !/usr/bin/env python3
|
||||
#
|
||||
# Copyright 2017-2018 Ettus Research, a National Instruments Company
|
||||
# Copyright 2025 Ettus Research, a National Instruments Company
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
"""Generate and TX samples using a set of waveforms, and waveform characteristics.
|
||||
"""Example to transmit waveforms using UHD python API.
|
||||
|
||||
This example transmits samples of dynamically generated, common waveforms,
|
||||
supporting sine, square, constant and ramp pattern. It's intended for use with
|
||||
a single device and supports transmitting the same waveform on multiple
|
||||
channels. The example implements two main operation modes that may not be
|
||||
supported by all USRPs devices and FPGA bitfile flavors.
|
||||
|
||||
1. Host based: This mode uses the MultiUSRP API
|
||||
<https://files.ettus.com/manual/page_coding.html#coding_api_hilevel> to stream
|
||||
a continuous tone waveform to the USRP and transmit it on the fly. This mode
|
||||
is supported by all USRPs and subjects the host's ability (which depends on CPU
|
||||
speed, network interface speed, CPU thread interruptions) to stream samples as
|
||||
fast as the USRP is transmitting them.
|
||||
|
||||
2. RFNoC Replay: This mode uses advanced API functions to buffer a waveform
|
||||
in the USRP's onboard DRAM before transmitting it. It utilizes capabilities of
|
||||
RFNoC (RF Network-on-Chip) <https://www.ettus.com/sdr-software/rfnoc/> together
|
||||
to create a dynamic RFNoC graph connecting multiple RFNoC blocks stream a
|
||||
waveform pattern from the host to DRAM and transmit the buffered waveform
|
||||
pattern from DRAM. This mode requires FPGA bitfiles that contain the RFNoC
|
||||
Replay Block and is not supported on all USRPs.
|
||||
|
||||
Example usage:
|
||||
tx_waveforms.py [--args addr=192.168.10.2] --freq 2.4e9 [--rate 1e6 --duration 10
|
||||
--channels 0 --wave-freq 1e4 --wave-ampl 0.3] [--dram]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@ -16,36 +40,105 @@ from uhd.usrp import dram_utils
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse the command line arguments."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-a", "--args", default="", type=str)
|
||||
parser.add_argument(
|
||||
"-w", "--waveform", default="sine", choices=["sine", "square", "const", "ramp"], type=str
|
||||
"""Parses the command line arguments.
|
||||
|
||||
Configuring the transmission, such as frequency, rate, duration,
|
||||
gain, waveform type, and whether to use DRAM for streaming.
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description=__doc__,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--args",
|
||||
type=str,
|
||||
default="",
|
||||
help='specifies the uhd device arguments, which holds multiple key value pairs separated by commas (e.g., addr=192.168.40.2,type=x300) [Default = ""]',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-w",
|
||||
"--waveform",
|
||||
choices=["sine", "square", "const", "ramp"],
|
||||
type=str,
|
||||
default="sine",
|
||||
help="specifies the waveform type [Default = sine]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--freq",
|
||||
type=float,
|
||||
required=True,
|
||||
help="specifies the RF center frequency in Hz (input is required)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--rate",
|
||||
type=float,
|
||||
default=1e6,
|
||||
help="specifies the sample rate in samples/seconds of the waveform generated at the host [Default = 1e6]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--duration",
|
||||
type=float,
|
||||
default=5.0,
|
||||
help="""specifies the transmit duration in seconds.
|
||||
If duration = -1 and --dram argument is used, the waveform pattern is repeated continuously until stopped.
|
||||
If duration > 0, the transmission stops after the configured duration.
|
||||
[Default = 0.5]""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--channels",
|
||||
nargs="+",
|
||||
type=int,
|
||||
default=0,
|
||||
help='specifies the channels to use (e.g., "0", "1", "0 1", etc) [Default = 0]',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
"--gain",
|
||||
type=int,
|
||||
default=10,
|
||||
help="specifies the gain for the RF chain in dB scale [Default = 10]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--wave-freq",
|
||||
type=float,
|
||||
default=1e4,
|
||||
help="specifies the waveform frequency in Hz [Default = 1e4]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--wave-ampl",
|
||||
type=float,
|
||||
default=0.3,
|
||||
help="specifies the waveform amplitude in the range [0 to 0.7] [Default = 0.3]",
|
||||
)
|
||||
parser.add_argument("-f", "--freq", type=float, required=True)
|
||||
parser.add_argument("-r", "--rate", default=1e6, type=float)
|
||||
parser.add_argument("-d", "--duration", default=5.0, type=float)
|
||||
parser.add_argument("-c", "--channels", default=0, nargs="+", type=int)
|
||||
parser.add_argument("-g", "--gain", type=int, default=10)
|
||||
parser.add_argument("--wave-freq", default=1e4, type=float)
|
||||
parser.add_argument("--wave-ampl", default=0.3, type=float)
|
||||
parser.add_argument(
|
||||
"--tx-delay",
|
||||
default=0.5,
|
||||
type=float,
|
||||
help="Delay before simultaneously starting transmission on all channels",
|
||||
default=0.5,
|
||||
help="specifies the delay in seconds (relative to when the command is executed) before simultaneously starting transmission on all channels [Default = 0.5]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dram", action="store_true", help="If given, will attempt to stream via DRAM"
|
||||
"--dram",
|
||||
action="store_true",
|
||||
help="specifies the operation mode (Host based or RFNoC Replay). If argument --dram is specified, the program will attempt to use RFNoC Replay mode, else Host Based",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def multi_usrp_tx(args):
|
||||
"""multi_usrp based TX example."""
|
||||
"""multi_usrp based TX example.
|
||||
|
||||
The function sets up and transmits a waveform using
|
||||
the uhd.usrp.MultiUSRP class. It configures the USRP device, generates a
|
||||
continuous tone waveform, streams it to the USRP and transmits it.
|
||||
"""
|
||||
usrp = uhd.usrp.MultiUSRP(args.args)
|
||||
if args.wave_freq == 0.0:
|
||||
desired_size = 1e6 # Just pick a value
|
||||
desired_size = 1e6 # Default size of the transmission buffer (or burst)
|
||||
else:
|
||||
desired_size = 10 * np.floor(args.rate / args.wave_freq)
|
||||
|
||||
@ -57,12 +150,12 @@ def multi_usrp_tx(args):
|
||||
"which is not supported on X410. Ignoring tx_delay."
|
||||
)
|
||||
else:
|
||||
# Delay transmission start. This will will allow SW to apply all
|
||||
# Delay transmission start. This will allow SW to apply all
|
||||
# configurations and start transmission on all channels simultaneously.
|
||||
# If tx_delay is to small, tx_start_time may be in the past by the time
|
||||
# If tx_delay is too small, tx_start_time may be in the past by the time
|
||||
# all configurations are applied and late command errors being reported.
|
||||
tx_start_time = usrp.get_time_now() + args.tx_delay
|
||||
|
||||
print("Generating waveform...")
|
||||
data = uhd.dsp.signals.get_continuous_tone(
|
||||
args.rate,
|
||||
args.wave_freq,
|
||||
@ -71,13 +164,23 @@ def multi_usrp_tx(args):
|
||||
max_size=(args.duration * args.rate),
|
||||
waveform=args.waveform,
|
||||
)
|
||||
print(
|
||||
f"Starting to stream waveform at the rate of {args.rate} samples/sec for the duration of {args.duration} seconds..."
|
||||
)
|
||||
usrp.send_waveform(
|
||||
data, args.duration, args.freq, args.rate, args.channels, args.gain, tx_start_time
|
||||
)
|
||||
print("Transmission complete.")
|
||||
|
||||
|
||||
def rfnoc_dram_tx(args):
|
||||
"""rfnoc_graph + replay-block based TX example."""
|
||||
"""rfnoc_graph + replay-block based TX example.
|
||||
|
||||
Refer Replay Block usage here <https://kb.ettus.com/Using_the_RFNoC_Replay_Block>.
|
||||
The function uses the RFNoC (RF Network-on-Chip) framework to transmit waveforms
|
||||
via DRAM. It initializes a graph, configures radio channels, sets rates and
|
||||
frequencies, uploads waveform data to DRAM, and starts streaming.
|
||||
"""
|
||||
# Init graph
|
||||
graph = uhd.rfnoc.RfnocGraph(args.args)
|
||||
if graph.get_num_mboards() > 1:
|
||||
@ -106,8 +209,9 @@ def rfnoc_dram_tx(args):
|
||||
|
||||
is_x410 = graph.get_mb_controller().get_mboard_name() == "x410"
|
||||
if not is_x410 and len(dram.radio_chan_pairs) > 1:
|
||||
# Use timed tuning for more than one channel on all devices except X410
|
||||
# X410 does not support timed tuning correct yet.
|
||||
# Use timed tuning for more than one channel to produce consistent
|
||||
# phase offsets between channels. This is supported on all devices
|
||||
# except X410, which does not support timed tuning correctly yet.
|
||||
cmd_time_offset = 0.1
|
||||
cmd_time = dram.radio_chan_pairs[0][0].get_time_now() + cmd_time_offset
|
||||
for radio, radio_chan in dram.radio_chan_pairs:
|
||||
@ -139,8 +243,8 @@ def rfnoc_dram_tx(args):
|
||||
and dram.replay_blocks[0].get_mem_size() / 4 < args.duration * args.rate
|
||||
):
|
||||
if len(data) < args.duration * args.rate:
|
||||
# If we are using this API, we need to upload the entire waveform,
|
||||
# we can't make use of looping over the same memory region over and
|
||||
# If we are using this API, then we need to upload the entire waveform.
|
||||
# We can't make use of looping over the same memory region over and
|
||||
# over again.
|
||||
data = np.tile(data, int(args.duration * args.rate // len(data)) + 1)
|
||||
data = data[: args.duration * args.rate]
|
||||
@ -149,7 +253,7 @@ def rfnoc_dram_tx(args):
|
||||
# DramTransmitter class.
|
||||
print(
|
||||
f"Uploading waveform data ({data.nbytes/(1024**2):.2f} MiB) "
|
||||
f"and starting streaming..."
|
||||
f"Starting to stream waveform at the rate of {args.rate} samples/sec for the duration of {args.duration} seconds..."
|
||||
)
|
||||
tx_md = uhd.types.TXMetadata()
|
||||
# do not use time spec if tx_delay is 0 or if we are using an X410
|
||||
@ -183,7 +287,9 @@ def rfnoc_dram_tx(args):
|
||||
else:
|
||||
stream_cmd.stream_now = False
|
||||
stream_cmd.time_spec = dram.radio_chan_pairs[0][0].get_time_now() + args.tx_delay
|
||||
print("Starting streaming...")
|
||||
print(
|
||||
f"Starting to stream waveform at the rate of {args.rate} samples/sec for the duration of {args.duration} seconds..."
|
||||
)
|
||||
dram.issue_stream_cmd(stream_cmd)
|
||||
if args.duration > 0:
|
||||
print("Waiting for transmission to complete...")
|
||||
@ -200,6 +306,7 @@ def rfnoc_dram_tx(args):
|
||||
async_md = None
|
||||
if not async_md:
|
||||
print("ERROR: Unable to receive ACK after burst!")
|
||||
print("Transmission complete.")
|
||||
else:
|
||||
print("Transmitting (Hit Ctrl-C to stop)...")
|
||||
try:
|
||||
@ -214,7 +321,7 @@ def rfnoc_dram_tx(args):
|
||||
|
||||
|
||||
def main():
|
||||
"""TX samples based on input arguments."""
|
||||
"""Transmit samples based on the input arguments."""
|
||||
args = parse_args()
|
||||
if not isinstance(args.channels, list):
|
||||
args.channels = [args.channels]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user