mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
118 lines
4.7 KiB
Python
118 lines
4.7 KiB
Python
|
|
#!/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", 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("--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, num_channels):
|
||
|
|
"""
|
||
|
|
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)
|
||
|
|
stream_cmd.stream_now = (num_channels == 1)
|
||
|
|
if num_channels > 1:
|
||
|
|
stream_cmd.time_spec = uhd.types.TimeSpec(usrp.get_time_now().get_real_secs() + INIT_DELAY)
|
||
|
|
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)
|
||
|
|
chan = 0
|
||
|
|
if args.rate:
|
||
|
|
print(f"Requesting sampling rate {args.rate/1e6} Msps...")
|
||
|
|
usrp.set_rx_rate(args.rate, chan)
|
||
|
|
actual_rate = usrp.get_rx_rate(chan)
|
||
|
|
print(f"Using sampling rate: {usrp.get_rx_rate(chan)/1e6} Msps.")
|
||
|
|
print(f"Requesting center frequency {args.freq/1e6} MHz...")
|
||
|
|
usrp.set_rx_freq(args.freq, chan)
|
||
|
|
print(f"Actual center frequency: {usrp.get_rx_freq(chan)/1e6} MHz.")
|
||
|
|
print(f"Requesting gain {args.gain} dB...")
|
||
|
|
usrp.set_rx_gain(args.gain, chan)
|
||
|
|
print(f"Actual gain: {usrp.get_rx_gain(chan)} dB.")
|
||
|
|
channels = check_channels(usrp, args)
|
||
|
|
if not channels:
|
||
|
|
return False
|
||
|
|
print("Selected {} RX channels.".format(', '.join(str(ch) for ch in channels)))
|
||
|
|
print("Generating RX streamer object...")
|
||
|
|
stream_args = uhd.usrp.StreamArgs("sc16", "sc16")
|
||
|
|
stream_args.channels = channels
|
||
|
|
stream_args.args = \
|
||
|
|
f"dest_addr={args.dest_addr},dest_port={args.dest_port}," \
|
||
|
|
f"stream_mode={'full_packet' if args.keep_hdr else 'raw_payload'}" + \
|
||
|
|
(f",adapter={args.adapter}" if args.adapter else "")
|
||
|
|
rx_streamer = usrp.get_rx_stream(stream_args)
|
||
|
|
print("Starting stream...")
|
||
|
|
num_channels = rx_streamer.get_num_channels()
|
||
|
|
stream_cmd = get_stream_cmd(usrp, actual_rate, args.duration, num_channels)
|
||
|
|
rx_streamer.issue_stream_cmd(stream_cmd)
|
||
|
|
print("Stream started. Press Ctrl-C to stop.")
|
||
|
|
if num_channels > 1:
|
||
|
|
args.duration += INIT_DELAY
|
||
|
|
timeout = time.monotonic() + args.duration if args.duration else None
|
||
|
|
try:
|
||
|
|
while timeout is None or time.monotonic() < timeout:
|
||
|
|
time.sleep(1)
|
||
|
|
except KeyboardInterrupt:
|
||
|
|
pass
|
||
|
|
print("Stopping stream...")
|
||
|
|
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())
|