# Rtlsdr
Elixir wrapper for [librtlsdr](https://github.com/steve-m/librtlsdr) - interface with RTL-SDR USB devices for software-defined radio applications.
## Features
- Full NIF bindings to librtlsdr
- GenServer-based device management with automatic resource cleanup
- Synchronous and asynchronous sample reading
- Streaming support with subscriber pattern
- IQ sample conversion utilities
- Support for all RTL-SDR tuner types (R820T, E4000, FC001x, etc.)
## Prerequisites
You need librtlsdr installed on your system:
### macOS
```bash
brew install librtlsdr
```
### Ubuntu/Debian
```bash
sudo apt-get install librtlsdr-dev
```
### Arch Linux
```bash
sudo pacman -S rtl-sdr
```
## Installation
Add `rtlsdr` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:rtlsdr, "~> 0.1.0"}
]
end
```
Then run:
```bash
mix deps.get
mix compile
```
## Quick Start
```elixir
# List available devices
Rtlsdr.list_devices()
#=> [%{index: 0, name: "Generic RTL2832U OEM", ...}]
# Start a device
{:ok, device} = Rtlsdr.Device.start_link(index: 0)
# Configure the device
:ok = Rtlsdr.Device.set_center_freq(device, 100_000_000) # 100 MHz
:ok = Rtlsdr.Device.set_sample_rate(device, 2_048_000) # 2.048 MS/s
:ok = Rtlsdr.Device.set_tuner_gain_mode(device, :manual)
:ok = Rtlsdr.Device.set_tuner_gain(device, 400) # 40.0 dB
# Read samples synchronously
{:ok, samples} = Rtlsdr.Device.read_samples(device, 262144)
# Convert to IQ float pairs
iq_samples = Rtlsdr.samples_to_iq(samples)
#=> [{0.0, 0.0039...}, {0.125, -0.0078...}, ...]
# Or start streaming
:ok = Rtlsdr.Device.start_streaming(device, self())
receive do
{:rtlsdr_samples, data} ->
# Process your samples here
IO.puts("Received #{byte_size(data)} bytes")
end
# Stop streaming
:ok = Rtlsdr.Device.stop_streaming(device)
# Close the device
:ok = Rtlsdr.Device.stop(device)
```
## IQ Sample Format
Samples are returned as a binary containing interleaved 8-bit unsigned I/Q data:
`<<I0, Q0, I1, Q1, ...>>`
Each I and Q value is in the range 0-255, with 127/128 being the center (zero) point.
To convert to normalized float values:
```elixir
# To list of {I, Q} tuples (-1.0 to 1.0)
Rtlsdr.samples_to_iq(samples)
# To flat list of floats [I1, Q1, I2, Q2, ...]
Rtlsdr.samples_to_floats(samples)
```
## Using the Streamer
For pub/sub style streaming with multiple consumers:
```elixir
{:ok, streamer} = Rtlsdr.Streamer.start_link(
device_index: 0,
center_freq: 100_000_000,
sample_rate: 2_048_000,
tuner_gain: 400
)
# Subscribe to receive samples
Rtlsdr.Streamer.subscribe(streamer)
# Start streaming
Rtlsdr.Streamer.start_streaming(streamer)
# Receive samples
receive do
{:rtlsdr_samples, data} -> process(data)
end
# Get statistics
{:ok, stats} = Rtlsdr.Streamer.stats(streamer)
#=> %{bytes_received: 1048576, samples_received: 4, ...}
```
## Device Configuration
### Frequency
```elixir
# Set center frequency (in Hz)
Rtlsdr.Device.set_center_freq(device, 433_920_000) # 433.92 MHz
# Get current frequency
{:ok, freq} = Rtlsdr.Device.get_center_freq(device)
```
### Sample Rate
Valid range: 225,001 - 3,200,000 Hz
```elixir
Rtlsdr.Device.set_sample_rate(device, 2_048_000)
# Common sample rates
Rtlsdr.common_sample_rates()
#=> [250000, 1024000, 1800000, 2048000, 2400000, 2560000, 2880000, 3200000]
```
### Gain Control
```elixir
# Get available gain values (in tenths of dB)
{:ok, gains} = Rtlsdr.Device.get_tuner_gains(device)
#=> [0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, ...]
# Set manual gain mode
Rtlsdr.Device.set_tuner_gain_mode(device, :manual)
# Set gain (in tenths of dB, so 400 = 40.0 dB)
Rtlsdr.Device.set_tuner_gain(device, 400)
# Or use automatic gain control
Rtlsdr.Device.set_tuner_gain_mode(device, :auto)
Rtlsdr.Device.set_agc_mode(device, true)
```
### Frequency Correction
```elixir
# Set PPM correction for crystal offset
Rtlsdr.Device.set_freq_correction(device, 56) # 56 ppm
```
### Direct Sampling (HF mode)
```elixir
# 0 = disabled (normal tuner mode)
# 1 = I-ADC input enabled
# 2 = Q-ADC input enabled
Rtlsdr.Device.set_direct_sampling(device, 1)
```
### Bias Tee
⚠️ **Warning**: Only enable if your setup requires bias tee power (e.g., for an LNA).
```elixir
Rtlsdr.Device.set_bias_tee(device, true)
```
## Supported Tuners
| Tuner | Frequency Range |
|-------|-----------------|
| R820T/R820T2 | 24 MHz - 1766 MHz |
| E4000 | 52 MHz - 2200 MHz (gap at 1100-1250 MHz) |
| FC0012 | 22 MHz - 948.6 MHz |
| FC0013 | 22 MHz - 1100 MHz |
| FC2580 | 146 MHz - 308 MHz |
## API Reference
### Rtlsdr
- `device_count/0` - Get number of connected devices
- `list_devices/0` - List all devices with info
- `find_device_by_serial/1` - Find device by serial number
- `samples_to_iq/1` - Convert samples to {I, Q} tuples
- `samples_to_floats/1` - Convert samples to flat float list
- `calculate_power_db/1` - Calculate signal power in dB
### Rtlsdr.Device
- `start_link/1` - Start a device process
- `stop/1` - Stop device and release hardware
- `info/1` - Get device info and current settings
- `set_center_freq/2`, `get_center_freq/1` - Frequency control
- `set_sample_rate/2`, `get_sample_rate/1` - Sample rate
- `set_tuner_gain/2`, `get_tuner_gain/1` - Gain control
- `set_tuner_gain_mode/2` - :auto or :manual gain
- `get_tuner_gains/1` - List supported gain values
- `get_tuner_type/1` - Get tuner chip type
- `set_freq_correction/2` - PPM correction
- `set_agc_mode/2` - RTL2832 AGC
- `set_direct_sampling/2` - HF direct sampling
- `set_bias_tee/2` - Bias tee power
- `read_samples/2` - Synchronous read
- `start_streaming/3`, `stop_streaming/1` - Async streaming
- `reset_buffer/1` - Clear sample buffer
### Rtlsdr.Streamer
- `start_link/1` - Start streamer with device
- `subscribe/2`, `unsubscribe/2` - Manage subscribers
- `start_streaming/1`, `stop_streaming/1` - Control streaming
- `set_center_freq/2`, `set_tuner_gain/2` - Runtime config
- `stats/1` - Get streaming statistics
## License
MIT License - see LICENSE file for details.
## Contributing
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -am 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request