README.md

# 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