uhd/host/examples/python/remote_rx.py
Marian Koop bc97db2847 examples: Fixed bugs observing cmd argument -c in remote_rx.py example
Fixed bug that used hardcoded channel variable to configure
device (rate, freq, gain), but channel list from argument during
streaming configuration.
Fixed bug when specifying multiple channels with duration argument
that resulted into python syntax error.
Changed multi channel behavior to use unique streamer per channel,
which now requires specifying dest-port per channel. This approach
ensures receiver implementation can associate data with a usrp
channel.
2024-01-03 08:56:28 -06:00

133 lines
5.5 KiB
Python
Executable file

#!/usr/bin/env python3
#
# Copyright 2022 Ettus Research, a National Instruments Brand
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
Stream IQ data from a USRP to a remote destination.
"""
import sys
import argparse
import time
import uhd
INIT_DELAY = 0.05 # 50mS initial delay before receive
def parse_args():
"""Parse the command line arguments"""
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--args", default="",
help="Device args (e.g., addr=192.168.40.2)")
parser.add_argument("-r", "--rate", type=float,
help="Output rate (samples/sec)")
parser.add_argument("-f", "--freq", type=float, required=True,
help="Center frequency")
parser.add_argument("-g", "--gain", type=float, default=0.0,
help="Gain (dB)")
parser.add_argument("-d", "--duration", default=None, type=float,
help="Stream duration (seconds), leave out to stream until stopped")
parser.add_argument("-c", "--channels", default=[0], nargs="+", type=int,
help="Which channel(s) to use (specify \"0\", \"1\", \"0 1\", etc)")
parser.add_argument("-i", "--dest-addr", type=str, required=True,
help="Remote destination IP address")
parser.add_argument("-p", "--dest-port", nargs="+", type=int, required=True,
help="Remote destination UDP port")
parser.add_argument("--adapter", type=str,
help="Adapter to use for remote streaming (e.g. 'sfp0')")
parser.add_argument("--dest-mac-addr",
help="Manually provide destination MAC address in the "
"format 01:a2:4f:6d:7e:5f. By default, the device will "
"use ARP to identify the MAC address.")
parser.add_argument("--keep-hdr", action="store_true",
help="Specify this argument to keep CHDR headers on outgoing packets")
return parser.parse_args()
def get_stream_cmd(usrp, rate, duration, start_time = None):
"""
Generate a stream command based on rate and duration.
"""
if duration:
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.num_done)
stream_cmd.num_samps = int(rate * duration)
else:
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.start_cont)
if start_time is None:
stream_cmd.stream_now = True
else:
stream_cmd.stream_now = False
stream_cmd.time_spec = start_time
return stream_cmd
def check_channels(usrp, args):
"""Check that the device has sufficient RX channels available"""
# Check that each channel specified is less than the number of total number of rx channels
# the device can support
channels = args.channels
dev_rx_channels = usrp.get_rx_num_channels()
if not all(map((lambda chan: chan < dev_rx_channels), channels)):
print("Invalid channel(s) specified.")
return []
return channels
def main():
"""
Go, go, go!
"""
args = parse_args()
usrp = uhd.usrp.MultiUSRP(args.args)
channels = check_channels(usrp, args)
if not channels:
return False
if len(channels) != len(args.dest_port):
print("Number of channels must match number of ports.")
return False
print("Selected RX channels: {}.".format(', '.join(str(ch) for ch in channels)))
if args.rate:
print(f"Requesting sampling rate {args.rate/1e6} Msps...")
for chan in channels:
usrp.set_rx_rate(args.rate, chan)
actual_rate = usrp.get_rx_rate(channels[0])
print(f"Using sampling rate: {actual_rate/1e6} Msps.")
print(f"Requesting center frequency {args.freq/1e6} MHz...")
for chan in channels:
usrp.set_rx_freq(args.freq, chan)
print(f"Actual center frequency: {usrp.get_rx_freq(channels[0])/1e6} MHz.")
print(f"Requesting gain {args.gain} dB...")
for chan in channels:
usrp.set_rx_gain(args.gain, chan)
print(f"Actual gain: {usrp.get_rx_gain(channels[0])} dB.")
print("Generating RX streamer object...")
rx_streamers = []
for chx, chan in enumerate(channels):
stream_args = uhd.usrp.StreamArgs("sc16", "sc16")
stream_args.channels = [chan]
stream_args.args = \
f"dest_addr={args.dest_addr},dest_port={args.dest_port[chx]}," \
f"stream_mode={'full_packet' if args.keep_hdr else 'raw_payload'}" + \
(f",adapter={args.adapter}" if args.adapter else "") + \
(f",dest_mac_addr={args.dest_mac_addr}" if args.dest_mac_addr else "")
rx_streamers.append(usrp.get_rx_stream(stream_args))
print("Starting stream(s)...")
start_time = uhd.types.TimeSpec(usrp.get_time_now().get_real_secs() + INIT_DELAY) if len(channels) > 1 else None
for rx_streamer in rx_streamers:
stream_cmd = get_stream_cmd(usrp, actual_rate, args.duration, start_time)
rx_streamer.issue_stream_cmd(stream_cmd)
print("Stream started. Press Ctrl-C to stop.")
timeout = time.monotonic() + args.duration if args.duration else None
if timeout and len(channels) > 1:
timeout += INIT_DELAY
try:
while timeout is None or time.monotonic() < timeout:
time.sleep(1)
except KeyboardInterrupt:
pass
print("Stopping stream...")
for rx_streamer in rx_streamers:
rx_streamer.issue_stream_cmd(uhd.types.StreamCMD(uhd.types.StreamMode.stop_cont))
print("Streaming complete. Exiting.")
return 0
if __name__ == "__main__":
sys.exit(main())