Direction of arrival estimation using commodity hardware

*concept*

- Get a few SDRs
- Synchronize them
- Use phase differences to locate signal sources

*step 1:*

*get a few SDRs*

- Go to ${ONLINE_MARKET}
- Search for RTL SDR
- Click buy
- Pay ~50€
- Receive four DVB-T sticks that can be used as software defined radio devices

*RTL SDR:*

- USB-Dongles, contain a Realtek 2832U DVB-T Decoder IC
- OEM software can play FM-Radio
- FM-radio feature is implemented using software-defined radio
- Started a "cheap SDR revolution", prices start at just 10€/dongle

*step 2:*

*synchronize them*

*Problem:*

- Receivers are built down to a prize
- Clock crystal frequencies may deviate
- Makes comparing signal phases difficult
- LO frequencies are different
- Sampling frequency is different

*Solution:*

- Feed the receivers from a single clock source
- Other frequencies are derived using PLLs

⇒ No frequency differences

*1st attempt*

- Used a NAND gate as an inverter to drive the crystal
- Fed two receivers as a proof of concept
- Had some signal integrity issues

*2nd attempt*

- Used a hex inverter to drive the crystal
- Fed four receivers for actual DOA estimation
- Also had signal integrity issues

*3rd attempt*

- Used the tuner's clock output
- Receivers are daisy-chained with short wires
- No more signal integrity issues

*3rd attempt*

- Used the tuner's clock output
- Receivers are daisy-chained with short wires
- No more signal integrity issues

*Drifting* between the receivers is fixed

But there are still *offsets* to compensate:

- Sample aquisition time
- LO-Phase

- Devices are not started at the exact same time

⇒ Start writing into their buffers at different times

- Samples at the same position in the buffer were not received at the same time
- High sample-rates & slow startup

⇒ offset may be thousands of samples

- Synchronize by calculating the cross-correlation and discarding samples
- Uses FFT for fast cross-correlation calculations over
`2¹⁸=262144`

samples

- Whole samples are discarded

⇒ Synchronization to an accuracy of one sample

⇒ Remaining offsets of up to ±0.5 Samples - Compensating these offsets in the time-domain

⇒ Fractional resampler (slow) - Shift in time domain ⇔ phase rotation in frequency domain

⇒ Offset can easily be compensated in frequency domain

- Further processing is performed in the frequency domain
- Downsampled to reduce processing load
- Algorithm uses phase-differences

⇒ Phase differences are calculated prior to downsampling

- Calculate the phase difference using the complex conjugate multiplication
- Downsample by averaging the phase differences

*Phase diagram before compensation:*

*Phase diagram after compensation:*

Remaining constant offset caused by LO-phase differences

⇒ compensated by subtracting a constant

*step 3:*

*direction estimation*

Use phase differences between antennas to estimate the source direction

Without noise the direction can be calculated from the phase difference, wavelength and antenna distance using simple trigonometry

In the presence of noise the phase differences are scaled down by a common constant

this makes calculating the direction a bit more difficult

*implementation*

Split into two parts:

- Fast multithreaded C-backend
- Talks to the SDRs using the V4L Linux kernel API
- Performs sample-accurate synchronization
- Calculates phase differences from FFT
- Downsamples

- Slow Python-frontend
- Performs offset compensations
- Calculates direction info
- Displays a GUI

To estimate the direction of arrival the program:

- Generates a vector of expected phase differences for every input direction to test
- Calculate the scalar dot product of these vectors and a vector of the measured phase vectors
- The test vector that is parralel to the measured phase vector corresponds to the direction of arrival
- The scalar dot product has a maximum if both vectors are parallel

```
1 def gen_position_matrix(self, wavelength):
2 …
3 for (idx, test_angle) in enumerate(test_angles):
4 rel_angles= edge_angles + test_angle
5 pos_mat[idx, :]= rel_wl * np.sin(rel_angles)
```

```
1 def get_direction_info(self, phases,
2 idx_start, idx_end):
3 …
4 pmat= self.get_position_matrix(wl_start, wl_end)
5 return(pmat @ phase_vector)
```

*conclusion*

- Directional resolution is not great
- Sometimes fails to lock
- Behaves strangely when indoors (reflections?)

Use FFT to split signal into bins

Use MUSIC to analyze these bins

The software sources are released under the GNU GPLv3 license:

The thesis and this presentation are released under the GNU FDLv1.3: