1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! Pure-Rust port of librtlsdr — RTL2832U USB control + tuner drivers.
//!
//! Talks to RTL-SDR dongles directly over USB via [`rusb`], without the
//! C `librtlsdr` library or its headers. Covers all five tuner families
//! shipped in real-world dongles (R820T / R820T2 / R828D, E4000,
//! FC0012, FC0013, FC2580).
//!
//! # Quick start
//!
//! ```no_run
//! use librtlsdr_rs::{RtlSdrDevice, RtlSdrError};
//!
//! # fn main() -> Result<(), RtlSdrError> {
//! // Open the first dongle plugged in.
//! let mut dev = RtlSdrDevice::open(0)?;
//!
//! // Tune to 100 MHz, 2.048 Msps.
//! dev.set_center_freq(100_000_000)?;
//! dev.set_sample_rate(2_048_000)?;
//!
//! // Manual gain at 14.4 dB (= 144 in tenths-of-dB).
//! dev.set_tuner_gain_mode(true)?;
//! dev.set_tuner_gain(144)?;
//!
//! // Read 64 KB of interleaved I/Q samples.
//! dev.reset_buffer()?;
//! let mut buf = vec![0u8; 65_536];
//! let n = dev.read_sync(&mut buf)?;
//! assert!(n > 0);
//! # Ok(())
//! # }
//! ```
//!
//! # Public surface
//!
//! The committed surface is intentionally narrow:
//!
//! - [`RtlSdrDevice`] — the device handle. All control + streaming
//! methods live here. Open via [`RtlSdrDevice::open`].
//! - Free enumeration helpers — [`get_device_count`],
//! [`get_device_name`], [`get_device_usb_strings`],
//! [`get_index_by_serial`].
//! - [`RtlSdrError`] — the unified error type returned by every
//! fallible operation.
//! - [`TunerType`] — the IC family identifier returned by
//! [`RtlSdrDevice::tuner_type`].
//!
//! Sample values are interleaved unsigned 8-bit I/Q pairs, the native
//! RTL-SDR format. Convert to centred `i8` (or `f32` in `[-1, 1]`) at
//! the consumer if needed; we don't impose a sample type on the read
//! path.
//!
//! # USB context + threading
//!
//! [`RtlSdrDevice`] holds an `Arc<rusb::DeviceHandle>` internally so
//! the device handle can be cloned across threads — `rusb::DeviceHandle`
//! is `Sync`, which makes the type *shareable* between threads. The
//! control methods on [`RtlSdrDevice`] take `&mut self` and serialise
//! on the caller; that's the supported single-thread pattern.
//!
//! For raw bulk reads on a worker thread (e.g. an `rtl_tcp`-style
//! server), call [`RtlSdrDevice::usb_handle`] to clone the underlying
//! handle and use [`RtlSdrDevice::BULK_ENDPOINT`] for the endpoint
//! address. **Note that `Sync` alone does not guarantee that
//! concurrent bulk and control transfers on the same handle are
//! safe** — `rusb`'s docs don't make that claim explicitly, and
//! libusb's caveats restrict per-resource concurrent access. If you
//! mix concurrent bulk and control on one handle, treat it as an
//! unsupported design assumption you've verified against your
//! libusb version + dongle hardware. The safer pattern is to
//! quiesce control calls (or do them all from one thread) while a
//! bulk-read worker is in flight.
//!
//! # Faithful-port note
//!
//! The crate is a port of the C `librtlsdr` source — register
//! addresses, magic constants, and per-tuner I2C tables are
//! transcribed directly from the upstream code. Some internal items
//! aren't currently called from Rust but are kept for completeness so
//! future hardware-feature work is a register-table read away rather
//! than a re-port. Hardware register manipulation requires extensive
//! integer casts inherent in a faithful port of C driver code.
//!
//! [`rusb`]: https://docs.rs/rusb
// Allow cast-heavy code throughout this crate (hardware register port)
// Faithful-port modules: `constants` and `reg` transcribe register
// addresses and hardware magic numbers from upstream `librtlsdr`'s C
// source. We keep the full table around even when not currently
// called from Rust so future hardware-feature work is a register-
// table read away rather than a re-port. Scoped `dead_code` allow
// (rather than crate-level) means accidental dead paths in `device`,
// `error`, or any future addition still get caught by the lint. Per
// #630 CR round 2.
pub
pub
// `tuner` is the internal abstraction layer over the five tuner-IC
// backends (R820T2 / E4000 / FC0012 / FC0013 / FC2580). The `Tuner`
// trait takes raw `rusb::DeviceHandle` parameters because the per-IC
// I2C transactions need direct USB control-transfer access — that's
// not a shape we want in the committed semver surface. External
// consumers control the tuner through `RtlSdrDevice` methods
// (`set_tuner_gain`, `set_tuner_bandwidth`, etc.) which dispatch
// internally to the right backend. Per #630 CR round 1.
pub
pub
pub use ;
pub use ;
// Re-export `Block` from the internal `reg` module. Block became
// part of the public API in 0.2 because `RtlSdrError::RegisterAccess`
// now carries `block: Block` for diagnostic context. Per #16.
pub use Block;
// Re-export `TunerType` from the internal `reg` module. The
// rendered docs use the source's own doc on `reg::TunerType`
// (which is more comprehensive than a one-line summary on the
// re-export). Per audit issue #19.
pub use TunerType;
// Re-export `rusb` so consumers pattern-matching on
// `RtlSdrError::Usb(rusb::Error::...)` can do so without taking a
// direct `rusb` dependency in their own `Cargo.toml` — and, more
// importantly, without risking a dep version mismatch where the
// consumer resolves a different `rusb` minor than this crate
// (which would make the inner `rusb::Error` types non-compatible
// at the type level). Per audit #15.
pub use rusb;