First, thanks for the great book! It helps me a lot to get started in SDR.
In the USRP chapter, you said that the UHD only supports fc64 and fc32 CPU data types when using the Python API. In my experiment, I found that all CPU data types are supported in python api, though you need to do some extra job when using the sc16 or sc8 as CPU format.
In short, the following codes can be use to rx and tx data with sc16 CPU data type in python.
Receiving
```python
import uhd
import numpy as np
usrp = uhd.usrp.MultiUSRP()
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("sc16", "sc16")
st_args.channels = [0]
metadata = uhd.types.RXMetadata()
streamer = usrp.get_rx_stream(st_args)
recv_buffer = np.zeros((1, 1000), dtype=np.int32) # use int32 to hold the iq sample
# 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
streamer.recv(recv_buffer, metadata)
# Stop Stream
stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.stop_cont)
streamer.issue_stream_cmd(stream_cmd)
samples = np.zeros(recv_buffer.shape[-1], dtype=np.complex64)
recv_buffer = recv_buffer.reshape(-1).view(np.int16) # view the buffer as i16,q16 array
samples.real = recv_buffer[::2]
samples.imag = recv_buffer[1::2]
print(len(samples))
print(samples[0:10])
```
When using `sc8` as CPU format, the `recv_buffer` needs to be initialized as `np.int16`, and be viewed as `np.int8` after receiving samples.
Transmitting
```python
import uhd
import numpy as np
# Configuration Parameters
tx_freq = 915e6 # Hz
tx_gain = 25 # Transmit gain in dB
tx_rate = 1e6 # Sample rate in samples per second
usrp = uhd.usrp.MultiUSRP()
st_args = uhd.usrp.StreamArgs("sc16", "sc16")
st_args.channels = [0]
# Set up TX streamer
tx_streamer = usrp.get_tx_stream(st_args)
# Configure USRP parameters
usrp.set_tx_rate(tx_rate)
usrp.set_tx_freq(uhd.libpyuhd.types.tune_request(tx_freq), 0)
usrp.set_tx_gain(tx_gain, 0)
# create random signal
samples = 0.1 * np.random.randn(10000) + 0.1j * np.random.randn(10000)
# convert the float complex samples to i16,q16 buffer
data = np.zeros(len(samples) * 2, dtype=np.int16)
data[::2] = np.real(samples) * 2 ** 15
data[1::2] = np.imag(samples) * 2 ** 15
data = data.view(np.int32)
# Create a metadata object for sending samples
md = uhd.types.TXMetadata()
md.start_of_burst = True
md.end_of_burst = False
# Stream the signal
tx_streamer.send(data, md)
# Indicate end of burst
md.start_of_burst = False
md.end_of_burst = True
# Send a few zeros to flush the buffer
tx_streamer.send(np.zeros((1000,), dtype=np.int32), md)
print("Transmission completed.")
```
When using `sc8` as CPU format, the `data` needs to be initialized as `np.int8`, be set via `np.real/imag(samples) * 2 ** 7`, and be viewed as `np.int16` before sending.
Why the code works?
Here is the underlying code of tx_streamer.send(nparr, md) in the UHD library. It shows that the UHD library only get the underlying bytes of the input numpy array and pass it to the cpp api, which means (I guess) we can use any numpy array as the input of the tx_streamer.send as long as the item size of the numpy array is same as the CPU format. So when using sc16 as CPU format, the input numpy array can be np.int32 or np.uint32, and when using sc8 as CPU format, the input numpy array can be np.int16 or np.uint16.
The C version of tx example also shows that the UHD library only cares about the underlying bytes of the input buffer.
First, thanks for the great book! It helps me a lot to get started in SDR.
In the USRP chapter, you said that the UHD only supports
fc64
andfc32
CPU data types when using the Python API. In my experiment, I found that all CPU data types are supported in python api, though you need to do some extra job when using thesc16
orsc8
as CPU format.In short, the following codes can be use to rx and tx data with
sc16
CPU data type in python.Receiving
```python import uhd import numpy as np usrp = uhd.usrp.MultiUSRP() 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("sc16", "sc16") st_args.channels = [0] metadata = uhd.types.RXMetadata() streamer = usrp.get_rx_stream(st_args) recv_buffer = np.zeros((1, 1000), dtype=np.int32) # use int32 to hold the iq sample # 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 streamer.recv(recv_buffer, metadata) # Stop Stream stream_cmd = uhd.types.StreamCMD(uhd.types.StreamMode.stop_cont) streamer.issue_stream_cmd(stream_cmd) samples = np.zeros(recv_buffer.shape[-1], dtype=np.complex64) recv_buffer = recv_buffer.reshape(-1).view(np.int16) # view the buffer as i16,q16 array samples.real = recv_buffer[::2] samples.imag = recv_buffer[1::2] print(len(samples)) print(samples[0:10]) ``` When using `sc8` as CPU format, the `recv_buffer` needs to be initialized as `np.int16`, and be viewed as `np.int8` after receiving samples.Transmitting
```python import uhd import numpy as np # Configuration Parameters tx_freq = 915e6 # Hz tx_gain = 25 # Transmit gain in dB tx_rate = 1e6 # Sample rate in samples per second usrp = uhd.usrp.MultiUSRP() st_args = uhd.usrp.StreamArgs("sc16", "sc16") st_args.channels = [0] # Set up TX streamer tx_streamer = usrp.get_tx_stream(st_args) # Configure USRP parameters usrp.set_tx_rate(tx_rate) usrp.set_tx_freq(uhd.libpyuhd.types.tune_request(tx_freq), 0) usrp.set_tx_gain(tx_gain, 0) # create random signal samples = 0.1 * np.random.randn(10000) + 0.1j * np.random.randn(10000) # convert the float complex samples to i16,q16 buffer data = np.zeros(len(samples) * 2, dtype=np.int16) data[::2] = np.real(samples) * 2 ** 15 data[1::2] = np.imag(samples) * 2 ** 15 data = data.view(np.int32) # Create a metadata object for sending samples md = uhd.types.TXMetadata() md.start_of_burst = True md.end_of_burst = False # Stream the signal tx_streamer.send(data, md) # Indicate end of burst md.start_of_burst = False md.end_of_burst = True # Send a few zeros to flush the buffer tx_streamer.send(np.zeros((1000,), dtype=np.int32), md) print("Transmission completed.") ``` When using `sc8` as CPU format, the `data` needs to be initialized as `np.int8`, be set via `np.real/imag(samples) * 2 ** 7`, and be viewed as `np.int16` before sending.Why the code works?
Here is the underlying code of
tx_streamer.send(nparr, md)
in the UHD library. It shows that the UHD library only get the underlying bytes of the input numpy array and pass it to the cpp api, which means (I guess) we can use any numpy array as the input of thetx_streamer.send
as long as the item size of the numpy array is same as the CPU format. So when usingsc16
as CPU format, the input numpy array can benp.int32
ornp.uint32
, and when usingsc8
as CPU format, the input numpy array can benp.int16
ornp.uint16
.The C version of tx example also shows that the UHD library only cares about the underlying bytes of the input buffer.