Introduction

This is a short introduction for getting started in the Linux environment using librtlsdr, MATLAB and GNU Radio. You have to use the RTL-SDR experimental branch from keenerd.

Hardware configuration and checking

Bias Tee activation

The default state of the system is noise but you need to activate the bias tee on the antenna switches.
ON: rtl_biast –d0 -b 1
ON: rtl_biast –d1 -b 1
ON: rtl_biast –d2 -b 1
You can start the SDR#, select the receiver and view/hear the noise:

Continuously check

The first simple check can be done using rtl_test that reads the samples from the single channel receivers and reports if any samples were lost during the transmission. You have to start all your single channel receivers and make the continuously check for the required sample rate.
rtl_test -d0 -s 2300000 &
rtl_test -d1 -s 2300000 &
rtl_test -d2 -s 2300000 &

If the samples are lost then it cannot be guaranteed that the receivers remain synchronised. Please see our support section for more details regarding the continuously check.
If the fifth dongle is added and you get the error message during the data transfer, e.g. “Failed to submit transfer 3!” “Library error -5, exiting…” Then, your Kernel limits the maximum size of all buffers that libusb can allocate to 16MB by default. In order to disable the limit, you have to run the following command as root:
echo 0 > /sys/module/usbcore/parameters/usbfs_memory_mb

rtl_sdr

rtl_sdr collects the I/Q samples and writes them to a file. You can create the script that starts all dongles to parallel capture of the samples, e.g.
rtl_sdr -d0 -f 1125000000 -g 35 -s 2500000 -n 50000000 -N FMcapture0-2.dat &
rtl_sdr -d1 -f 1125000000 -g 35 -s 2500000 -n 50000000 -N FMcapture1-2.dat &
rtl_sdr -d2 -f 1125000000 -g 35 -s 2500000 -n 50000000 -N FMcapture2-2.dat &
Please see the help section of the rtl_sdr about more details and parameters.

RTL-SDR support from MATLAB

With Communications System Toolbox™ Support Package for RTL-SDR Radio, you can use MATLAB® and Simulink® to design and prototype systems that process real time wireless signals. There is also a free ebook available on the MathWorks site that helps to learn how to receive and analyze wireless signals using RTL-SDR, MATLAB, and Simulink.

The raw, captured IQ data can also be saved into file and opened in MATLAB using the standard file functions. Please see the basic introduction prepared by Dr. Aaron Scher from Oregon Institute of Technology.

Load samples in Matlab

The samples collected by rtl_sdr provided as unsigned interleaved bytes. You can use the loadFile.m function that loads the sequence of I and Q and stores them as complex matlab vector:

function y = loadFile(filename)
% y = loadFile(filename)
% reads complex samples from the rtlsdr file
fid = fopen(filename,’rb’);
y = fread(fid,’uint8=>double’);
y = y-127;
y = y(1:2:end) + i*y(2:2:end);

Calculating the cross-correlation

Matlab has the built in xcorr function that returns the cross-correlation of two discrete-time sequences, x and y. Cross-correlation measures the similarity between x and shifted (lagged) copies of y as a function of the lag. For example, the following script loads two captured files and calculated the cross-correlation of the samples every 100.000 samples.

a=loadFile(‘FMcapture0-2.dat’);
fprintf (‘%f\n’, size(a));

c=loadFile(‘FMcapture2-2.dat’);
fprintf (‘%f\n’, size(b));
%samples difference
outtime=[];
%phase difference
outph=[];
for i=1:480
offset=100000;
cuta = a(i*offset:(i+1)*offset);
cutb = c(i*offset:(i+1)*offset);
xcorres = xcorr (cuta, cutb);
%Alternative for faster proceeding; “offset” variable must be set accordingly to FFT size
%xcorres=ifft(fft(cuta,131072).*conj(fft(cutb,131072)));

[maxval, maxindx] = max(xcorres);
anglec=radtodeg(angle(maxval));
outtime(i)=offset-maxindx;
outph (i) = anglec;
end

The results can be plotted in the Matlab. Please see the support section for more information regarding the phase drift.

RTL-SDR support from GNU Radio

GNU Radio is a free software development toolkit that provides signal processing blocks to implement software-defined radios and signal-processing systems. It is widely used in hobbyist, academic, and commercial environments to support both wireless communications research and real-world radio systems.

Below we provide some examples how you can use different options in order to get cross-correlated data in GNU Radio.

General Input/Output

GNU Radio supports: File incl. FIFO, Sockets incl. TCP/UDP and distributed messaging based on ZeroMQ. You need to calculate and align the streams before sedning to the GNU Radio.

Direction Finding

You can use a 4-channel Entry Level Receiver for these experiments (e.g. https://coherent-receiver.com/wp-content/uploads/2018/02/DirectionFindingSystem.jpg).

  1. Identify all receivers and set custom serial numbers
  2. Tune the supervisor to the FM-Station and test that it works
  3. Connect the single channel receiver to the USB: you need to have connected following devices: noise generator; supervisor and single channel receiver
  4. https://coherent-receiver.com/support; Please take a look at the section Management of the antenna switch and noise generator cards:
    • Try to switch the bias_tee on on the receiver. Bias_tee is the power supply of the antenna switch card (antenna switch does not work without power supply). You can use the existing program rtl_biast for this proposes
      • ON: rtl_biast -dX -b 1
      • OFF: rtl_biast -dX –b 0
    • X is the number of the dongle (0, 1, 2, 3); please note; the supervisor don’t have the antenna switch but the command will work (it changes only the GPIO state)
    • As a result the led “software selectable bias_tee” must be switched on/off depending on the command
    • You do the same programmatically using the GPIO:
      // true – bias on; false – bias off
      setGPIOBit(handle, (byte) 0x01, true);
  5. Switch the antenna switch between noise and antenna input; please see sections: “Antenna switch and noise generator card management” and “I2C and RTL2832U”
  6. Start a SDR program; select the supervisor – you should be able to listen FM-radio; select another receiver – you have to get noise signal
  7. You need to send the I2C command to the noise generator/antenna switch in order to switch from noise to antenna. You need to read the specification, understand the pseudocode below and write you own routine:
    • //http://www.nxp.com/documents/data_sheet/PCA9534.pdf
    • Specification
    • Device Address:
    • 0100 000 0 = 0x40 – Write; 0x41 – Read
    • Register 3: configure as an output; 0 = corresponding pin enabled as an output
    • Register 1: LED: 0 is on; 1 is off
    • pins description is in the schema, e.g. 0x01 – d6 is on; all other are off
    • //Enable I2C Repeater
    • enableI2CRepeater(handle, true);
    • //Write I2C register
    • writeI2CRegister (handle, (byte) 0x40, (byte) 0x03, (byte) 0x00, false )
    • //ox00 – FM; 0x1f – noise
    • writeI2CRegister (handle, (byte) 0x40, (byte) 0x01, (byte) 0x00, true )
    • The result should be: the led indicator (switch state) on the antenna switch will be on/off and you have to hear the FM radio instead of noise. Please note: you should to send the I2C-command to the supervisor (not to the receiver!). Therefore, please choose the write handle.
  8. If you were successful with the previous step; please connect all receivers and switch the state for all receivers between the noise and antenna input
  9. You can use GNU Radio and Ettus Blocks for direction finding routines; please install GnuRadio and EttusBlocks https://github.com/EttusResearch/gr-doa
  10. Data synchronisation
    • In order to align the data, you need to calculate the cross-correlation function; please read the sources in Internet or links in the getting started section but you will need to do something like this:
    • //– FFT A — –;
    • fft.complexForward(a);
    • //– FFT B — –;
    • fft.complexForward(b);
    • // — Conjugate B — —
    • CrossCorrFFT.conjugate (b);
    • // Element-wise multiplication
    • float[] c = CrossCorrFFT.elemwiseMult(a,b);
    • // — IFFT — –;
    • fft.complexInverse(c, true);
    • Complex max = CrossCorrFFT.getMax(c);
    • You can visualize the result in GNURadio in order to see the typical cross-correlation peak
  11. You can do all this steps in the GNURadio using block; please check it itself
  12. Please note; that you need to calculate the cross-correlation function and find maximum by switched noise-on;
  13. Next step is to send the intervealed aligned IQ-data to GNURadio. You can do it via TCP, FileSource or ZMQ. ZMQ is most preferred option.
  14. Please create the following diagram in GNU Radio: https://coherent-receiver.com/wp-content/uploads/2017/09/ExternalCorrelation-2.png
  15. You can start gnuradio and have to see something like this:
    https://coherent-receiver.com/wp-content/uploads/2016/09/music2.png
  16. The next steps will be: install properly 4 antennas and provide the direction finding signal. Please do all cross-correlation and alignment on the same frequency as the DF-signal-source.

Direction Finding (easy version)

You can use a 4-channel Entry Level Receiver for these experiments (e.g. https://coherent-receiver.com/wp-content/uploads/2018/02/DirectionFindingSystem.jpg). You will need only 2 channels and a supervisor for this experiment. Moreover, you have to install two antennas spaced 1/2 wavelength apart. You can use an ordinary walkie-talkie as a signal source.

This example is based on the gnuradio-doa blocks. Please take a look at the presentation from Sam Whiting explaining gnuradio flowgraphs and OOT modules for DOA analysis and phase synchronisation before you start.

The necessary steps are:

  1. Identify all receivers and set custom serial numbers
  2. Connect antenna and tune the supervisor to the local FM-Station and test that it works
  3. https://coherent-receiver.com/support; Please take a look at the section Management of the antenna switch and noise generator cards:
    • Enable the bias_tee on all receivers with antenna switches. Bias_tee is the power supply of the antenna switch card (antenna switch does not work without power supply). You can use the existing program rtl_biast for this proposes
      • ON: rtl_biast -dX -b 1
      • OFF: rtl_biast -dX –b 0
    • X is the number of the dongle (0, 1, 2, 3); please note; the supervisor don’t have the antenna switch but the command will work (it changes only the GPIO state)
    • As a result the led “software selectable bias_tee” must be switched on/off depending on the command
    • You do the same programmatically using the GPIO:
      // true – bias on; false – bias off
      setGPIOBit(handle, (byte) 0x01, true);
  4. Switch the antenna switch between noise and antenna input; please see sections: “Antenna switch and noise generator card management” and “I2C and RTL2832U”
  5. Start a SDR program; select the supervisor – you should be able to listen FM-radio; select another receiver – you have to get noise signal
  6. You can use the above mentioned program rtl_biast for the supervisor management
    • rtl_biast -d 2 -a 0x40 -v 0x00 //RF input
    • rtl_biast -d 2 -a 0x40 -v 0x1f //Noise input or Input from noise expansion
  7. The result should be: the led indicator (switch state) on the antenna switch will be on/off and you have to hear the FM radio instead of noise. Please note: you should to send the I2C-command to the supervisor (not to the receiver!). Therefore, please choose the write handle.
  8. If you were successful with the previous step; please connect all receivers and switch the state for all receivers between the noise and antenna input to the noise state
  9. You can use GNU Radio and gnuradio-doa blocks from Sam Whiting for direction finding routines; you have to set the dithering_off in case of R820T2. You can do it as follows: ret = rtlsdr_set_dithering(_dev, 0);
    if (ret) {
    fprintf(stderr, "dithering off - failure\n");
    } else {
    fprintf(stderr, "dithering off - success\n");
    }
  10. The simplest DOA realisation is based on 2 channels: min_coh_2. This example includes cross-correlation and DoA-routines. Therefore, you just need to start it and after cross-correlation set the antenna switches to the RF-input using the rtl_biast. There are several tutorials that describes all details of PCA  incl. math (eigenvalue and eigenvector, covariance matrix etc.).

Walkie-talkie is turned off

Walkie-talkie is turned on

Python support

Python is very well suited for rapid prototyping. Compared to MatLab, all functionalities do not ship with Python. But the language has a huge selection of libraries (batteries included), e.g. for linear algebra so that developed algorithms can be tested on real data. We added necessary functionalities to the standard Python wrapper (pyrtlsdr) in order to support coherent receiver hardware. The modified python wrapper can be found on github. Please note that you have to use our custom modification of the Keenerd rtl-sdr library. Python wrapper includes several examples based on the synchronous or native Python 3 asynchronous modes:

Initialisation:

freq=443000000 #frequency
gain=0 #gain
I2CADDR=0x40 #I2C Adress from the clock card
REG3=0x03 #see support section for register description
REG1=0x01 #see support section for register description
num_devices=RtlSdr.get_device_count() #number of devices
slaves=list()
supervisors=list()

for dev_num in range (num_devices):

try:
device=RtlSdr(dev_num)
device.set_dithering(False)
device.set_center_freq(freq)
device.set_sample_rate(240000)
device.set_gain(gain)
device.set_bias_tee(True)
enable_noise_output(device, True)
except I2CException as err: #device is working correct but cannot send i2c command to the supervisor->this is slave
device.set_i2c_repeater(False)
slaves.append(…)
except Exception as e:
print (“Error during initialisation”, str(e))
raise (e)
sys.exit(-1)
else:
supervisors.append (…) #no exception -> this is a supervisor

if not supervisors or not slaves:
print (“supervisors or slaves lists are empty; please check the hardware”)
sys.exit (-1)

def enable_noise_output (dev, enable):

#this is just an example for 8-bit register; you can connect further devices to i2c, e.g. a display like SSD1306 or manage more sophisticated hardware

if enable:
#set the output to noise
dev.set_i2c_repeater(True)
dev.write_i2c_reg(I2CADDR, REG3, 0) #see support section for further details
dev.write_i2c_reg(I2CADDR, REG1, 0x1f)
dev.set_i2c_repeater(False)

else:
#set the output to RF
dev.set_i2c_repeater(True)
dev.write_i2c_reg(I2CADDR, REG3, 0)
dev.write_i2c_reg(I2CADDR, REG1, 0)
dev.set_i2c_repeater(False)

Samples reading

#Every loop provides intervealing samples from all slaves that can be send to consumer, e.g. using rxpy or observer pattern
async def read (dev_list):
async for samples in AsyncZip (*dev_list, yield_when=asyncio.ALL_COMPLETED):
….

Cross-correlation

Cross-correlation calculation can be done as follows:
async def calc_xcorr (devs):
samples1= …
for j in range (1, len(devs)):
samples2= …
corr=fft.ifft (fft.fft(samples2)*(np.conj(fft.fft(samples1))))
# alternative (seems to be slower)
# corr = signal.fftconvolve(samples1, np.conj(samples2[::-1]))

peakpos = np.argmax(np.abs(corr))
# print (“dev “, j, “correlation peak:”, peakpos, cmath.polar(corr[peakpos]))
# print (“dev “, j, “angle”, np.angle(corr[peakpos]))

Send to consumer

We recommend to use ZeroMQ for the inegration with GNU Radio and provide the aligned and phased corrected interleaved samples:
context1 = zmq.Context()
socket1 = context1.socket(zmq.PUB)
socket1.bind(“tcp://127.0.0.1:5559”)

Additional GNU Radio modules for coherent experiments

EttusResearch/gr-doa – Direction-of-Arrival (DoA) Demo for GNU Radio (OOT)

External Interesting Resources

Software defined radio implementation of passive RADAR using low-cost DVB-T receivers – gnuradio configuration for passive radar and phase synchronisation

AI and Multichannel Receivers

RF-based direction finding using multichannel coherent receivers and machine learning algorithms is a very interesting research topic. Please contact us or provide a link to your publications/research group if you are doing research in this area. We think (may be wrongly) that first steps should be done in the traditional way and the machine learning methods have to be applied from the autocorrelation matrix. Some known publications to this topic are provided below:

Samith Abeywickrama, Lahiru Jayasinghe, Hua Fu and Chau Yuen “RF-Based Direction Finding of UAVs Using DNN”

Tathagata Mukherjee, Michael Duckett, Piyush Kumar, Jared Devin Paquet, Daniel Rodriguez, Mallory Haulcomb, Kevin George, and Eduardo Pasiliao “RSSI-Based Supervised Learning for Uncooperative Direction-Finding”