-
Notifications
You must be signed in to change notification settings - Fork 459
Debugging dropped samples and identifying achievable sample rates
This page describes a few ways to determine the sample rates your computer can keep up with, when using the bladeRF.
Unfortunately, not all setups are created equal. Some USB 3.0 controllers work far better than others, and results may vary across drivers and OSes.
If you're in the market for a USB 3.0 controller to use with the bladeRF, you'll probably want to take a look at the lists of working and problematic configurations. It's also recommended that you talk to other bladeRF users on the forum or IRC channel to learn about their experiences with various setups.
Reminder: USB 2.0 has a nominal max speed of 480 mbps (60 MB/s). With 32-bits samples (i.e., the bladeRF SC16 Q11 DAC format), this corresponds to a nominal max of 15 Msps. In reality, however, the max achievable rate over USB 2.0 with the bladeRF has been reported to fall within 5-8 Msps (some folks have reported up to 10 Msps).
As of FPGA bitstreams hostedx40 and hostedx115 v0.0.3:
- LED1 turns off momentarily when an RX underrun occurs
- LED3 turns off momentarily when a TX overrun occurs
Below are some general tips for achieving better sample rates:
- Store samples in RAM rather than pulling them from disk, when possible.
- In *nix, store input/output samples in /dev/shm
- If that's not possible, favor an SSD over an HDD
- Disable CPU frequency scaling
- For example, in Ubuntu 13.10, one can do the following for each CPU $N (e.g., 0 through 3 for 4 cores)
echo performance | sudo tee /sys/devices/system/cpu/cpu$N/cpufreq/scaling_governor
- For example, in Ubuntu 13.10, one can do the following for each CPU $N (e.g., 0 through 3 for 4 cores)
- Ensure that your processors are running at their full speeds, and disable power saving or on-demand frequency scaling.
- When writing applications with libbladeRF's asynchronous interface, avoid any "costly" operations in callbacks.
- Treat these likes you would interrupt service routines. Copy sample data and update any necessary status. Even better - don't copy sample data at all. Keep track of which buffers are in-flight versus those that application code is currently using. Avoid performing long calculations or file I/O.
Using the following methods over a number of different sample rates, you can get a rough idea as to where your setup's max achievable sample rate.
In version 0.0.3 of the hostedx40.rbf and hostedx115 FPGA builds, the 32-bits of IQ data can be replaced with a 32-bit monotonically increasing counter value, via bit 9 of the FX3 -> FPGA GPIO interface.
We can enable this mode and capture samples via the bladeRF-cli program. Again, it's advised to save samples to RAM rather than to a disk. Here we'll save 80 MB worth of samples at 4Msps:
$ bladeRF-cli -i bladeRF> load fpga ~/projects/bladeRF-files/hostedx40_v0.0.3.rbf Loading fpga from /home/jon/projects/bladeRF-files/hostedx40_v0.0.3.rbf... Done. bladeRF> set samplerate rx 4M Setting RX sample rate - req: 4000000 0/1Hz, actual: 4000000 0/1Hz bladeRF> print gpio GPIO: 0x00000057 LMS Enable: Enabled LMS RX Enable: Enabled LMS TX Enable: Enabled TX Band: Low Band (300M - 1.5GHz) RX Band: Low Band (300M - 1.5GHz) RX Source: LMS6002D bladeRF> set gpio 0x257 bladeRF> print gpio GPIO: 0x00000257 LMS Enable: Enabled LMS RX Enable: Enabled LMS TX Enable: Enabled TX Band: Low Band (300M - 1.5GHz) RX Band: Low Band (300M - 1.5GHz) RX Source: Internal 32-bit counter bladeRF> rx config file=/dev/shm/samples_4msps.bin n=10M bladeRF> rx start bladeRF> rx State: Running Last error: None File: /dev/shm/samples_4msps.bin File format: SC16 Q11, Binary # Samples: 10485760 # Buffers: 32 # Samples per buffer: 32768 # Transfers: 16 Timeout (ms): 1000 bladeRF> rx State: Idle Last error: None File: /dev/shm/samples_4msps.bin File format: SC16 Q11, Binary # Samples: 10485760 # Buffers: 32 # Samples per buffer: 32768 # Transfers: 16 Timeout (ms): 1000 bladeRF> quit
Note that after setting GPIO bit 9, the RX source changed from LMS6002D to Internal 32-bit counter.
If you look at the /dev/shm/samples_4msps.bin with a hex editor/viewer, you should see that the 32-bit words are incrementing. The following python script may be used to look for gaps in samples. Gaps should not occur; if they do, it's indicative of the host not keeping up with the specified sample rate.
#!env python3 import sys import array if len(sys.argv) != 2: print('Usage: ' + sys.argv[0] + ': <data file>') sys.exit(1) with open(sys.argv[1], 'rb') as in_file: data = in_file.read() count = array.array('I') count.fromstring(data) fail = 0 curr = count[0] for i in range(1, len(count)): exp = curr + 1 if count[i] != exp: print('[' + str(i) + '] = ' + str(count[i]) + ', Expected ' + str(exp) + ', Gap = ' + str(exp - count[i])) fail += 1 curr = count[i] print('Number of gaps:' + str(fail))
NOTE: It's been reported the first block of samples may be leftover in FPGA/FX3 buffers from a previous reception. This is a known issue, and does not indicate dropped samples. In this case, you'll see 1 gap reported, and it will be a gap between the first and second block of samples, where a block is the value shown above in "# Samples per buffer."
A crude test is to transmit a tone, and look for symptoms of discontinuities:
- An unexpectedly and extremely "noisy" or "messy" spectrum
- Gaps in the RF envelope, resulting from underrun in the FPGA
- Obvious gaps in the IQ waveform over time
- A "messy" IQ polar plot
Below is a simple python script to generate a single period of a sine. The frequency of this will be a function of the sample rate at which it's transmitted: sample_rate / n_samples
For example, for a 1 period of sine constructed of 1024 samples @ 4 MHz: 4e6 sample/second / 1024 samples = 3.90625 KHz
#!env python3 import sys import math # Number of samples should be a multiple of 1024 to match bladeRF # buffer size constraints n_samples = 1024 # IQ values are in the range [-2048, 2047]. Clamp to 1800 just to # avoid saturating scale = 1800 if (len(sys.argv) < 2): print('Usage: ' + sys.argv[0] + ': <output file> [n_samples]\n') sys.exit(1) if (len(sys.argv) > 2): try: n_samples = int(sys.argv[2]) except ValueError: print('Invalid value for n_samples: ' + sys.argv[2] + '\n') sys.exit(1) if n_samples < 1024 or n_samples % 1024 != 0: print('n_samples must be a multiple of 1024\n') sys.exit(1) with open(sys.argv[1], 'w') as out_file: for n in range(0, n_samples): theta = n * (2 * math.pi) / n_samples i = int(scale * math.cos(theta)) q = int(scale * math.sin(theta)) out_file.write(str(i) + ', ' + str(q) + '\n')
To transmit these samples on "infinite" repeat, until the bladeRF-cli is closed:
$ cd /dev/shm $ ./tone_iq.py samples.csv $ bladeRF-cli -i bladeRF> set frequency tx 1G bladeRF> set samplerate tx 4M bladeRF> tx config file=samples.csv format=csv repeat=0 bladeRF> tx start ... Run as long as needed ... bladeRF> quit
To achieve the same results with osmocom_siggen, run the following and set the "Frequency" field to 3.90625K.
osmocom_siggen -f 1G -s 4M -a bladerf=0 --sine