Uploaded by labib sharrar

Project Guidelines (Labib Sharrar)

advertisement
PROJECT NOTES AND GUIDELINES
Student Name: Labib Sharrar
PYTHON PROGRAMMING FOR THE USRP
Stage 1: Installation of the USRP Toolchain and the Python API
1. Install an Ubuntu 22.04 virtual machine. For the virtual machine allocate at least 4 GB RAM (6 GB to
be safe), 40 GB of hard disk space and at least 4 CPU cores. Once the Ubuntu virtual machine is
installed, Guest Additions in the operating system. This will allow USB access for the USRP boards.
Refer to the following video tutorials for precise instructions:
https://www.youtube.com/watch?v=x5MhydijWmc&t=953s&ab_channel=ProgrammingKnowledge
https://www.youtube.com/watch?v=zdkl16oAS1k&ab_channel=ProgrammingKnowledge2
2. Once all the installations and setups are complete based on the above tutorials, open a terminal in the
virtual OS, type the following commands:
sudo apt-get update
sudo apt-get install python3-pip
3. Next, install a series of libraries by using the command below:
sudo apt-get -y install autoconf automake build-essential ccache cmake
cpufrequtils doxygen ethtool fort77 g++ gir1.2-gtk-3.0 git gobjectintrospection gpsd gpsd-clients inetutils-tools libasound2-dev libboostall-dev libcomedi-dev libcppunit-dev libfftw3-bin libfftw3-dev libfftw3doc libfontconfig1-dev libgmp-dev libgps-dev libgsl-dev liblog4cpp5-dev
libncurses5 libncurses5-dev libpulse-dev libqt5opengl5-dev libqwt-qt5-dev
libsdl1.2-dev libtool libudev-dev libusb-1.0-0 libusb-1.0-0-dev libusb-dev
libxi-dev libxrender-dev libzmq3-dev libzmq5 ncurses-bin python3-cheetah
python3-click python3-click-plugins python3-click-threading python3-dev
python3-docutils python3-gi python3-gi-cairo python3-gps python3-lxml
python3-mako python3-numpy python3-numpy-dbg python3-opengl python3-pyqt5
python3-requests python3-scipy python3-setuptools python3-six python3sphinx python3-yaml python3-zmq python3-ruamel.yaml swig wget
4. Install the PyBOMBS library which is good for building the USRP toolchain from source. Type the
following commands in the terminal:
pip3 install --upgrade git+https://github.com/gnuradio/pybombs.git
pybombs auto-config
pybombs recipes add-defaults
pybombs prefix init ~/gnuradio
source ~/gnuradio/setup_env.sh
5. Finally, type the following to install the UHD library:
pybombs install uhd
6. Do not close the terminal once the installation is complete. Type python3 and then import uhd. If
there is no error, then the installation is complete.
7. Go to the home/gnuradio/setup_env.sh file, copy all the code and add them to the ~/.bashrc file. To
do this, type nano ~/.bashrc. This will take you to the bashrc.sh file on Ubuntu. Press Ctrl+v+w to go
to the end to file. Then paste the copied lines from home/gnuradio/setup_env.sh file. Press Ctrl+o to
save and Ctrl+x to exit.
8. After doing the above step, close the terminal and open a new one. Type python3 and try to import
uhd. If there are no errors, then the USRP toolchain and the Python API and fully installed.
Stage 2: Coding the for the USRP
1. Open Visual Studio Code and install the python extension and code runner.
2. In the IDE type the following:
import uhd
usrp = uhd.usrp.MultiUSRP()
samples = usrp.recv_num_samps(10000, 100e6, 1e6, [0], 50) # units: N, Hz, Hz,
list of channel IDs, dB
print(samples[0:10])
The codes shown here are mostly for reception by the USRP. The “recv_num_samps()”, above is
Python code that tunes the USRP to 100 MHz, using a sample rate of 1 MHz, and grabs 10,000 samples
off the USRP, using a receive gain of 50 dB. The [0] is telling the USRP to use its first input port, and
only receive one channel worth of samples (for a B210 to receive on two channels at once, for example,
you could use [0, 1]). If you are trying to receive at a high rate but are getting overflows (O’s are
showing up in your console). Instead of usrp = uhd.usrp.MultiUSRP(), use:
usrp = uhd.usrp.MultiUSRP("num_recv_frames=1000")
This makes the receive buffer much larger (the default value is 32), helping to reduce overflows. The
actual size of the buffer in bytes depends on the USRP and type of connection, but simply
setting num_recv_frames to a value much higher than 32 tends to help.
However, for more serious applications, it is recommended that the following code be used.
import uhd
import numpy as np
usrp = uhd.usrp.MultiUSRP()
num_samps = 10000 # number of samples received
center_freq = 100e6 # Hz
sample_rate = 1e6 # Hz
gain = 50 # dB
usrp.set_rx_rate(sample_rate, 0)
usrp.set_rx_freq(uhd.libpyuhd.types.tune_request(center_freq), 0)
usrp.set_rx_gain(gain, 0)
# Set up the stream and receive buffer
st_args = uhd.usrp.StreamArgs("fc32", "sc16")
st_args.channels = [0]
metadata = uhd.types.RXMetadata()
streamer = usrp.get_rx_stream(st_args)
recv_buffer = np.zeros((1, 1000), dtype=np.complex64)
# Start Stream
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.start_cont)
stream_cmd.stream_now = True
streamer.issue_stream_cmd(stream_cmd)
# Receive Samples
samples = np.zeros(num_samps, dtype=np.complex64)
for i in range(num_samps//1000):
streamer.recv(recv_buffer, metadata)
samples[i*1000:(i+1)*1000] = recv_buffer[0]
# Stop Stream
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.stop_cont)
streamer.issue_stream_cmd(stream_cmd)
print(len(samples))
print(samples[0:10])
3. In order to transmit signals from the USRP, the following code can be used:
import uhd
import numpy as np
usrp = uhd.usrp.MultiUSRP()
samples = 0.1*np.random.randn(10000) + 0.1j*np.random.randn(10000) # create
random signal
duration = 10 # seconds
center_freq = 915e6
sample_rate = 1e6
gain = 20 # [dB] start low then work your way up
usrp.send_waveform(samples, duration, center_freq, sample_rate, [0], gain)
Similar to the recv_num_samps() convenience function, UHD provides the send_waveform() function
to transmit a batch of samples. If you specify a duration (in seconds) longer than the provided signal,
it will simply repeat it. It helps to keep the values of samples between -1.0 and 1.0.
4. If you have connected an external 10 MHz and PPS source to your USRP, you will want to make sure
to call these two lines after initializing your USRP:
usrp.set_clock_source("external")
usrp.set_time_source("external")
If you are using an onboard GPSDO, you will instead use:
usrp.set_clock_source("gpsdo")
usrp.set_time_source("gpsdo")
On the frequency sync side there’s not much else to do; the LO used in the USRP’s mixer is now
going to be tied to the external source or GPSDO. But on the timing side, you may wish to command
the USRP to start sampling exactly on the PPS, for example. This can be done with the following
code:
# copy the receive example above, everything up until # Start Stream
# Wait for 1 PPS to happen, then set the time at next PPS to 0.0
time_at_last_pps = usrp.get_time_last_pps().get_real_secs()
while time_at_last_pps == usrp.get_time_last_pps().get_real_secs():
time.sleep(0.1) # keep waiting till it happens- if this while loop never
finishes then the PPS signal isn't there
usrp.set_time_next_pps(uhd.libpyuhd.types.time_spec(0.0))
# Schedule Rx of num_samps samples exactly 3 seconds from last PPS
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.num_done)
stream_cmd.num_samps = num_samps
stream_cmd.stream_now = False
stream_cmd.time_spec = uhd.libpyuhd.types.time_spec(3.0) # set start time (try
tweaking this)
streamer.issue_stream_cmd(stream_cmd)
# Receive Samples. recv() will return zeros, then our samples, then more zeros,
letting us know it's done
waiting_to_start = True # keep track of where we are in the cycle (see above
comment)
nsamps = 0
i = 0
samples = np.zeros(num_samps, dtype=np.complex64)
while nsamps != 0 or waiting_to_start:
nsamps = streamer.recv(recv_buffer, metadata)
if nsamps and waiting_to_start:
waiting_to_start = False
elif nsamps:
samples[i:i+nsamps] = recv_buffer[0][0:nsamps]
i += nsamps
If it seems like it’s not working, but is not throwing any errors, try changing that 3.0 number from
anything between 1.0 and 5.0. You can also check the metadata after the call to recv(), simply
check if metadata.error_code != uhd.types.RXMetadataErrorCode.none:.
For debugging sake, you can verify the 10 MHz signal is showing up to the USRP by checking the
return of usrp.get_mboard_sensor("ref_locked", 0). If the PPS signal isn’t showing up, you’ll know it
because the first while loop in the code above will never finish.
C++ PROGRAMMING FOR THE USRP
Stage 1: C++ coding for the USRP
1. Include the following headers in the .cpp file:
#include <uhd/utils/thread_priority.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/exception.hpp>
#include <uhd/types/tune_request.hpp>
#include <boost/program_options.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include <iostream>
2. Defines a safe wrapper that places a catch-all around main. If an exception is thrown, it prints to
stderr and returns.
int UHD_SAFE_MAIN(int argc, char *argv[]) {
...
}
3. Set the scheduling priority on the current thread. Same as set_thread_priority but does not throw
on failure.
uhd::set_thread_priority_safe();
4. Create variables as shown below:
std::string
std::string
std::string
std::string
double
double
double
double
device_args("addr=192.168.10.2");
subdev("A:0");
ant("TX/RX");
ref("internal");
rate(1e6);
freq(915e6);
gain(10);
bw(1e6);
5. Creating USRP Object
uhd::usrp::multi_usrp::sptr usrp =
uhd::usrp::multi_usrp::make(device_args);
6. Set the clock source for the usrp device. This sets the source for a 10 MHz reference clock.
usrp->set_clock_source(ref)
7. Set the RX frontend specification. The subdev spec maps a physical part of a daughter-board to a
channel number. Set the subdev spec before calling into any methods with a channel number.
usrp->set_rx_subdev_spec(subdev)
8. Get a printable summary for this USRP configuration.
usrp->get_pp_string()
9. Set the RX sample rate. The rate is in Samples Per Second.
usrp->set_rx_rate(rate)
10. Gets the RX sample rate. Returns the rate is in Samples Per Second.
usrp->get_rx_rate()
11. Create a tune request, with the RF frequency in Hz. Set the RX center frequency.
uhd::tune_request_t tune_request(freq)
usrp->set_rx_freq(tune_request)
12. Get the RX center frequency. Returns the frequency in Hz.
usrp->get_rx_freq()
13. Set the RX gain value for the specified gain element. For an empty name, distribute across all
gain elements. Sets the gain in dB.
usrp->set_rx_gain(gain)
14. Get the RX gain value for the specified gain element. For an empty name, sum across all gain
elements. Returns the gain in dB.
usrp->get_rx_gain()
15. Set the RX bandwidth on the frontend. Sets the bandwidth in Hz.
usrp->set_rx_bandwidth(bw)
16. Get the RX bandwidth on the frontend. Returns the bandwidth in Hz.
usrp->get_rx_bandwidth()
17. Select the RX antenna on the frontend.
usrp->set_rx_antenna(ant)
18. Get the selected RX antenna on the frontend. Returns the antenna name.
usrp->get_rx_antenna()
19. Exiting:
return EXIT_SUCCESS;
20. Full C++ Example Code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
<uhd/utils/thread_priority.hpp>
<uhd/utils/safe_main.hpp>
<uhd/usrp/multi_usrp.hpp>
<uhd/exception.hpp>
<uhd/types/tune_request.hpp>
<boost/program_options.hpp>
<boost/format.hpp>
<boost/thread.hpp>
<iostream>
int UHD_SAFE_MAIN(int argc, char *argv[]) {
uhd::set_thread_priority_safe();
std::string
std::string
std::string
std::string
double
double
double
double
device_args("addr=192.168.10.2");
subdev("A:0");
ant("TX/RX");
ref("internal");
rate(1e6);
freq(915e6);
gain(10);
bw(1e6);
//create a usrp device
std::cout << std::endl;
std::cout << boost::format("Creating the usrp device with: %s...") %
device_args << std::endl;
uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(device_args);
// Lock mboard clocks
std::cout << boost::format("Lock mboard clocks: %f") % ref << std::endl;
usrp->set_clock_source(ref);
//always select the subdevice first, the channel mapping affects the other
settings
std::cout << boost::format("subdev set to: %f") % subdev << std::endl;
usrp->set_rx_subdev_spec(subdev);
std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() <<
std::endl;
//set the sample rate
if (rate <= 0.0) {
std::cerr << "Please specify a valid sample rate" << std::endl;
return ~0;
}
// set sample rate
std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6) <<
std::endl;
usrp->set_rx_rate(rate);
std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp>get_rx_rate() / 1e6) << std::endl << std::endl;
// set freq
std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq / 1e6) <<
std::endl;
uhd::tune_request_t tune_request(freq);
usrp->set_rx_freq(tune_request);
std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp>get_rx_freq() / 1e6) << std::endl << std::endl;
// set the rf gain
std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl;
usrp->set_rx_gain(gain);
std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain()
<< std::endl << std::endl;
// set the IF filter bandwidth
std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (bw / 1e6) <<
std::endl;
usrp->set_rx_bandwidth(bw);
std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % (usrp>get_rx_bandwidth() / 1e6) << std::endl << std::endl;
// set the antenna
std::cout << boost::format("Setting RX Antenna: %s") % ant << std::endl;
usrp->set_rx_antenna(ant);
std::cout << boost::format("Actual RX Antenna: %s") % usrp->get_rx_antenna()
<< std::endl << std::endl;
return EXIT_SUCCESS;
}
Download