I am interested in acquiring multiple channels with my rig using the Python API. This is my setup: An x310 with two TwinRX , connections are 10 GB Ethernet for each TwinRX with a dedicated network card for each one (IPs are 192.168.30.2 and 192.168.40.2). I have UHD v4.6.0.0.0 installed and Ubuntu 22.04 with Python 3.10.12.
I use this code to receive samples from a single TwinRX and it works fine:
import argparse
import numpy as np
import uhd
import gc
import subprocess
def parse_args():
"""Parse the command line arguments"""
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--args", default="", type=str,
help="Additional arguments for the USRP device")
parser.add_argument("-o", "--output-file", type=str, required=True,
help="Output file prefix where samples will be saved")
parser.add_argument("-f", "--freq", type=float, required=True,
help="Center frequency in Hz")
parser.add_argument("-r", "--rate", default=1e6, type=float,
help="Sample rate in Hz (default: 1e6)")
parser.add_argument("-d", "--duration", default=5.0, type=float,
help="Duration of reception in seconds (default: 5.0)")
parser.add_argument("-c", "--channels", default=[0, 1], nargs="+", type=int,
help="List of channel IDs to receive from (default: 0 1)")
parser.add_argument("-g", "--gain", type=int, default=10,
help="Gain in dB (default: 10)")
parser.add_argument("-n", "--numpy", default=False, action="store_true",
help="Save output file in NumPy format (default: No)")
parser.add_argument("-i", "--ip-address", type=str, required=True,
help="IP address of the USRP device")
return parser.parse_args()
def get_board_number(ip_address):
"""Extract board number from IP address"""
parts = ip_address.split('.')
return parts[-2] # Extract the second-to-last part of the IP address
def main():
"""RX samples and write to file"""
args = parse_args()
usrp = uhd.usrp.MultiUSRP(f"addr={args.ip_address} {args.args}")
usrp.set_rx_bandwidth(args.rate, 0)
num_samps = int(np.ceil(args.duration * args.rate))
board_number = get_board_number(args.ip_address)
# Receive samples for both channels simultaneously
samps = usrp.recv_num_samps(num_samps, args.freq, args.rate, args.channels, args.gain)
print(f"Bandwidth: {usrp.get_rx_bandwidth(0)/1e6:.0f} MHz")
print(f"Format of samples: {type(samps[0, 1])}")
print(f"Shape of samples: {samps.shape}")
print(f"Number of channels: {samps.shape[0]}")
print(f"Number of samples per channel: {samps.shape[1]}")
print(f"Size per .iq file: {samps.dtype.itemsize * num_samps / 1e6:.0f} MB")
for channel_id, samples in zip(args.channels, samps):
output_filename = f"{args.output_file}_{board_number}_{channel_id}.iq"
print(f"Saving samples for TwinRX 192.168.{board_number}.2, channel {channel_id} to {output_filename}")
samples.tofile(output_filename)
if __name__ == "__main__":
main()
But now I want to acquire samples from both twinRX simultaneously. Apparently threading doesn’t work due to GIL limitations, but when I try to do multiprocessing with the following code I get errors:
import numpy as np
import uhd
import gc
import subprocess
import multiprocessing
# Hardcoded parameters
output_file = "muestras_prueba"
freq = 218.64e6
rate = 25e6
gain = 50
duration = 10
channels = [0, 1]
ips = ["192.168.30.2", "192.168.40.2"]
def get_board_number(ip_address):
"""Extract board number from IP address"""
parts = ip_address.split('.')
return parts[-2] # Extract the second-to-last part of the IP address
def acquire_and_save_samples(ip_address, output_file, freq, rate, gain, duration, channels):
"""Acquire samples from USRP and save to file"""
try:
usrp = uhd.usrp.MultiUSRP(f"addr={ip_address}")
usrp.set_rx_bandwidth(rate, 0)
num_samps = int(np.ceil(duration * rate))
board_number = get_board_number(ip_address)
# Receive samples for both channels simultaneously
samps = usrp.recv_num_samps(num_samps, freq, rate, channels, gain)
print(f"Bandwidth: {usrp.get_rx_bandwidth(0)/1e6:.0f} MHz")
print(f"Format of samples: {type(samps[0, 1])}")
print(f"Shape of samples: {samps.shape}")
print(f"Number of channels: {samps.shape[0]}")
print(f"Number of samples per channel: {samps.shape[1]}")
print(f"Size per .iq file: {samps.dtype.itemsize * num_samps / 1e6:.0f} MB")
for channel_id, samples in zip(channels, samps):
output_filename = f"{output_file}_{board_number}_{channel_id}.iq"
print(f"Saving samples for TwinRX 192.168.{board_number}.2, channel {channel_id} to {output_filename}")
samples.tofile(output_filename)
except Exception as e:
print(f"Error occurred with IP {ip_address}: {e}")
def main():
"""Main function to handle multiprocessing for sample acquisition"""
processes = []
for ip in ips:
p = multiprocessing.Process(target=acquire_and_save_samples, args=(ip, output_file, freq, rate, gain, duration, channels))
processes.append(p)
p.start()
for p in processes:
p.join()
if __name__ == "__main__":
try:
main()
finally:
command = "sync; echo 3 > /proc/sys/vm/drop_caches"
subprocess.run(['sudo', 'sh', '-c', command])
print("Caché freed successfully.")
gc.collect()
print("RAM freed successfully")
[WARNING] [RFNOC] chdr_ctrl_endpoint: Received async message for unknown destination. Source EPID: 2 Destination port: 7
[ERROR] [RFNOC::GRAPH] Error during initialization of block 0/Radio#1!
free(): double free detected in tcache 2
[ERROR] [RFNOC::GRAPH] An exception has been detected while initializing the graph: RfnocError: OpTimeout: Control operation timed out waiting for ACK
Error occurred with IP 192.168.30.2: RuntimeError: Failed to create rfnoc_graph.
user26334772 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.