mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
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.
133 lines
5.5 KiB
Python
Executable file
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())
|