python: Update pyuhd wrapper functions to make freq tuning use timedcmds

This commit is contained in:
Marian Koop 2024-05-22 15:25:01 +02:00 committed by Joerg Hofrichter
parent 5de584251c
commit 52467d3261

View File

@ -7,7 +7,10 @@
Python UHD module containing the MultiUSRP and other objects
"""
import time
import numpy as np
from .. import libpyuhd as lib
@ -17,14 +20,17 @@ def _get_mpm_client(token, mb_args):
MultiUSRP.
"""
from uhd.utils import mpmtools
rpc_addr = mb_args.get('mgmt_addr')
rpc_port = mb_args.get('rpc_port', mpmtools.MPM_RPC_PORT)
rpc_addr = mb_args.get("mgmt_addr")
rpc_port = mb_args.get("rpc_port", mpmtools.MPM_RPC_PORT)
return mpmtools.MPMClient(mpmtools.InitMode.Hijack, rpc_addr, rpc_port, token)
class MultiUSRP(lib.usrp.multi_usrp):
"""
MultiUSRP object for controlling devices
"""
def __init__(self, args=""):
"""MultiUSRP constructor"""
super(MultiUSRP, self).__init__(args)
@ -32,18 +38,12 @@ class MultiUSRP(lib.usrp.multi_usrp):
# MPM client
if self.get_tree().exists("/mboards/0/token"):
token = self.get_tree().access_str("/mboards/0/token").get()
mb_args = \
self.get_tree().access_device_addr("/mboards/0/args").get().to_dict()
setattr(self, 'get_mpm_client', lambda: _get_mpm_client(token, mb_args))
mb_args = self.get_tree().access_device_addr("/mboards/0/args").get().to_dict()
setattr(self, "get_mpm_client", lambda: _get_mpm_client(token, mb_args))
def recv_num_samps(self,
num_samps,
freq,
rate=1e6,
channels=(0,),
gain=10,
start_time=None,
streamer=None):
def recv_num_samps(
self, num_samps, freq, rate=1e6, channels=(0,), gain=10, start_time=None, streamer=None
):
"""
RX a finite number of samples from the USRP
@ -63,6 +63,7 @@ class MultiUSRP(lib.usrp.multi_usrp):
one locally and attempt to destroy it afterwards.
:return: numpy array of complex floating-point samples (fc32)
"""
def _config_streamer(streamer):
"""
Set up the correct streamer
@ -72,6 +73,7 @@ class MultiUSRP(lib.usrp.multi_usrp):
st_args.channels = channels
streamer = super(MultiUSRP, self).get_rx_stream(st_args)
return streamer
def _start_stream(streamer):
"""
Issue the start-stream command.
@ -83,8 +85,10 @@ class MultiUSRP(lib.usrp.multi_usrp):
stream_cmd.time_spec = start_time
else:
stream_cmd.time_spec = lib.types.time_spec(
super(MultiUSRP, self).get_time_now().get_real_secs() + 0.05)
super(MultiUSRP, self).get_time_now().get_real_secs() + 0.05
)
streamer.issue_stream_cmd(stream_cmd)
def _stop_stream(streamer):
"""
Issue the stop-stream command and flush the queue.
@ -94,6 +98,7 @@ class MultiUSRP(lib.usrp.multi_usrp):
streamer.issue_stream_cmd(stream_cmd)
while streamer.recv(recv_buffer, metadata):
pass
## And go!
# Configure USRP
for chan in channels:
@ -105,8 +110,7 @@ class MultiUSRP(lib.usrp.multi_usrp):
metadata = lib.types.rx_metadata()
# Set up buffers and counters
result = np.empty((len(channels), num_samps), dtype=np.complex64)
recv_buffer = np.zeros(
(len(channels), streamer.get_max_num_samps()), dtype=np.complex64)
recv_buffer = np.zeros((len(channels), streamer.get_max_num_samps()), dtype=np.complex64)
recv_samps = 0
samps = 0
# Now stream
@ -117,8 +121,7 @@ class MultiUSRP(lib.usrp.multi_usrp):
print(metadata.strerror())
if samps:
real_samps = min(num_samps - recv_samps, samps)
result[:, recv_samps:recv_samps + real_samps] = \
recv_buffer[:, 0:real_samps]
result[:, recv_samps : recv_samps + real_samps] = recv_buffer[:, 0:real_samps]
recv_samps += real_samps
# Stop and clean up
_stop_stream(streamer)
@ -126,15 +129,17 @@ class MultiUSRP(lib.usrp.multi_usrp):
streamer = None
return result
def send_waveform(self,
waveform_proto,
duration,
freq,
rate=1e6,
channels=(0,),
gain=10,
start_time=None,
streamer=None):
def send_waveform(
self,
waveform_proto,
duration,
freq,
rate=1e6,
channels=(0,),
gain=10,
start_time=None,
streamer=None,
):
"""
TX a finite number of samples from the USRP
:param waveform_proto: numpy array of samples to TX
@ -149,6 +154,7 @@ class MultiUSRP(lib.usrp.multi_usrp):
one locally and attempt to destroy it afterwards.
:return: the number of transmitted samples
"""
def _config_streamer(streamer):
"""
Set up the correct streamer
@ -158,20 +164,38 @@ class MultiUSRP(lib.usrp.multi_usrp):
st_args.channels = channels
streamer = super(MultiUSRP, self).get_tx_stream(st_args)
return streamer
## And go!
for chan in channels:
super(MultiUSRP, self).set_tx_rate(rate, chan)
super(MultiUSRP, self).set_tx_freq(lib.types.tune_request(freq), chan)
if start_time is None:
super(MultiUSRP, self).set_tx_freq(lib.types.tune_request(freq), chan)
super(MultiUSRP, self).set_tx_gain(gain, chan)
if start_time is not None:
# Use timed commands to set frequency to minimize phase differences
# between channels. Picking a command time in the middle between
# now and start time.
cmd_time_offset = max(
0, (start_time - super(MultiUSRP, self).get_time_now()).get_real_secs() / 2
)
cmd_time = super(MultiUSRP, self).get_time_now() + cmd_time_offset
super(MultiUSRP, self).set_command_time(cmd_time)
for chan in channels:
super(MultiUSRP, self).set_tx_freq(lib.types.tune_request(freq), chan)
super(MultiUSRP, self).clear_command_time()
# Enough time for tune time to expire
time.sleep(cmd_time_offset)
# Configure streamer
streamer = _config_streamer(streamer)
# Set up buffers and counters
buffer_samps = streamer.get_max_num_samps()
proto_len = waveform_proto.shape[-1]
if proto_len < buffer_samps:
waveform_proto = np.tile(waveform_proto,
(1, int(np.ceil(float(buffer_samps)/proto_len))))
waveform_proto = np.tile(
waveform_proto, (1, int(np.ceil(float(buffer_samps) / proto_len)))
)
proto_len = waveform_proto.shape[-1]
send_samps = 0
max_samps = int(np.floor(duration * rate))
@ -185,7 +209,7 @@ class MultiUSRP(lib.usrp.multi_usrp):
metadata.time_spec = start_time
metadata.has_time_spec = True
while send_samps < max_samps:
real_samps = min(proto_len, max_samps-send_samps)
real_samps = min(proto_len, max_samps - send_samps)
if real_samps < proto_len:
samples = streamer.send(waveform_proto[:, :real_samps], metadata)
else: