Skip to main content

hackrf_nusb/
config.rs

1//! Validated high-level HackRF transceiver configuration.
2
3use crate::errors::{Error, Result};
4
5const DEFAULT_FREQUENCY_HZ: u64 = 900_000_000;
6const DEFAULT_SAMPLE_RATE_HZ: u32 = 10_000_000;
7const DEFAULT_LNA_GAIN_DB: u8 = 8;
8const DEFAULT_VGA_GAIN_DB: u8 = 20;
9const DEFAULT_TX_VGA_GAIN_DB: u8 = 0;
10
11/// Validated HackRF transceiver configuration.
12#[derive(Clone, Debug, Eq, PartialEq)]
13pub struct Config {
14    frequency_hz: u64,
15    sample_rate_hz: u32,
16    lna_gain_db: u8,
17    vga_gain_db: u8,
18    tx_vga_gain_db: u8,
19    amp_enabled: bool,
20    bias_tee_enabled: bool,
21}
22
23impl Default for Config {
24    fn default() -> Self {
25        Self {
26            frequency_hz: DEFAULT_FREQUENCY_HZ,
27            sample_rate_hz: DEFAULT_SAMPLE_RATE_HZ,
28            lna_gain_db: DEFAULT_LNA_GAIN_DB,
29            vga_gain_db: DEFAULT_VGA_GAIN_DB,
30            tx_vga_gain_db: DEFAULT_TX_VGA_GAIN_DB,
31            amp_enabled: false,
32            bias_tee_enabled: false,
33        }
34    }
35}
36
37impl Config {
38    /// Start building a validated transceiver configuration.
39    pub fn builder() -> ConfigBuilder {
40        ConfigBuilder::default()
41    }
42
43    /// Tuned center frequency in Hz.
44    pub const fn frequency_hz(&self) -> u64 {
45        self.frequency_hz
46    }
47
48    /// Complex IQ sample rate in Hz.
49    pub const fn sample_rate_hz(&self) -> u32 {
50        self.sample_rate_hz
51    }
52
53    /// MAX2837 RX IF/LNA gain in dB.
54    pub const fn lna_gain_db(&self) -> u8 {
55        self.lna_gain_db
56    }
57
58    /// MAX2837 baseband/VGA gain in dB.
59    pub const fn vga_gain_db(&self) -> u8 {
60        self.vga_gain_db
61    }
62
63    /// MAX2837 transmit VGA gain in dB.
64    pub const fn tx_vga_gain_db(&self) -> u8 {
65        self.tx_vga_gain_db
66    }
67
68    /// Whether the RF amplifier is enabled.
69    pub const fn amp_enabled(&self) -> bool {
70        self.amp_enabled
71    }
72
73    /// Whether antenna-port bias power is requested.
74    pub const fn bias_tee_enabled(&self) -> bool {
75        self.bias_tee_enabled
76    }
77
78    pub(crate) fn set_frequency_hz_internal(&mut self, value: u64) {
79        self.frequency_hz = value;
80    }
81
82    pub(crate) fn set_sample_rate_hz_internal(&mut self, value: u32) {
83        self.sample_rate_hz = value;
84    }
85
86    pub(crate) fn set_lna_gain_db_internal(&mut self, value: u8) {
87        self.lna_gain_db = value;
88    }
89
90    pub(crate) fn set_vga_gain_db_internal(&mut self, value: u8) {
91        self.vga_gain_db = value;
92    }
93
94    pub(crate) fn set_tx_vga_gain_db_internal(&mut self, value: u8) {
95        self.tx_vga_gain_db = value;
96    }
97
98    pub(crate) fn set_amp_enabled_internal(&mut self, value: bool) {
99        self.amp_enabled = value;
100    }
101
102    pub(crate) fn set_bias_tee_enabled_internal(&mut self, value: bool) {
103        self.bias_tee_enabled = value;
104    }
105}
106
107/// Builder for [`Config`].
108#[derive(Clone, Debug, Default)]
109pub struct ConfigBuilder {
110    config: Config,
111}
112
113impl ConfigBuilder {
114    /// Set the center frequency in Hz.
115    pub fn frequency_hz(mut self, value: u64) -> Self {
116        self.config.frequency_hz = value;
117        self
118    }
119
120    /// Set the complex IQ sample rate in Hz.
121    pub fn sample_rate_hz(mut self, value: u32) -> Self {
122        self.config.sample_rate_hz = value;
123        self
124    }
125
126    /// Set RX IF/LNA gain in dB.
127    pub fn lna_gain_db(mut self, value: u8) -> Self {
128        self.config.lna_gain_db = value;
129        self
130    }
131
132    /// Set baseband/VGA gain in dB.
133    pub fn vga_gain_db(mut self, value: u8) -> Self {
134        self.config.vga_gain_db = value;
135        self
136    }
137
138    /// Set TX VGA gain in dB.
139    pub fn tx_vga_gain_db(mut self, value: u8) -> Self {
140        self.config.tx_vga_gain_db = value;
141        self
142    }
143
144    /// Enable or disable the RF amplifier.
145    pub fn amp_enable(mut self, enabled: bool) -> Self {
146        self.config.amp_enabled = enabled;
147        self
148    }
149
150    /// Enable or disable antenna-port bias power.
151    pub fn bias_tee(mut self, enabled: bool) -> Self {
152        self.config.bias_tee_enabled = enabled;
153        self
154    }
155
156    /// Validate and return the configuration.
157    pub fn build(self) -> Result<Config> {
158        validate_config(&self.config)?;
159        Ok(self.config)
160    }
161}
162
163pub(crate) fn validate_config(config: &Config) -> Result<()> {
164    validate_frequency(config.frequency_hz)?;
165    validate_sample_rate(config.sample_rate_hz)?;
166    validate_lna_gain(config.lna_gain_db)?;
167    validate_vga_gain(config.vga_gain_db)?;
168    validate_tx_vga_gain(config.tx_vga_gain_db)?;
169    Ok(())
170}
171
172pub(crate) fn validate_frequency(value: u64) -> Result<()> {
173    if !(1_000_000..=6_000_000_000).contains(&value) {
174        return Err(Error::invalid_config(
175            "frequency_hz",
176            "must be between 1 MHz and 6 GHz inclusive",
177        ));
178    }
179    Ok(())
180}
181
182pub(crate) fn validate_sample_rate(value: u32) -> Result<()> {
183    if !(2_000_000..=20_000_000).contains(&value) {
184        return Err(Error::invalid_config(
185            "sample_rate_hz",
186            "must be between 2 MHz and 20 MHz inclusive",
187        ));
188    }
189    Ok(())
190}
191
192pub(crate) fn validate_lna_gain(value: u8) -> Result<()> {
193    if value > 40 || !value.is_multiple_of(8) {
194        return Err(Error::invalid_config(
195            "lna_gain_db",
196            "must be 0 through 40 dB in 8 dB steps",
197        ));
198    }
199    Ok(())
200}
201
202pub(crate) fn validate_vga_gain(value: u8) -> Result<()> {
203    if value > 62 || !value.is_multiple_of(2) {
204        return Err(Error::invalid_config(
205            "vga_gain_db",
206            "must be 0 through 62 dB in 2 dB steps",
207        ));
208    }
209    Ok(())
210}
211
212pub(crate) fn validate_tx_vga_gain(value: u8) -> Result<()> {
213    if value > 47 {
214        return Err(Error::invalid_config(
215            "tx_vga_gain_db",
216            "must be between 0 dB and 47 dB inclusive",
217        ));
218    }
219    Ok(())
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225
226    #[test]
227    fn defaults_match_hackrf_transfer() {
228        let config = Config::default();
229        assert_eq!(config.frequency_hz(), 900_000_000);
230        assert_eq!(config.sample_rate_hz(), 10_000_000);
231        assert_eq!(config.lna_gain_db(), 8);
232        assert_eq!(config.vga_gain_db(), 20);
233        assert_eq!(config.tx_vga_gain_db(), 0);
234        assert!(!config.amp_enabled());
235        assert!(!config.bias_tee_enabled());
236    }
237
238    #[test]
239    fn validates_documented_ranges_and_steps() {
240        assert!(Config::builder().frequency_hz(999_999).build().is_err());
241        assert!(
242            Config::builder()
243                .frequency_hz(6_000_000_001)
244                .build()
245                .is_err()
246        );
247        assert!(Config::builder().sample_rate_hz(1_999_999).build().is_err());
248        assert!(
249            Config::builder()
250                .sample_rate_hz(20_000_001)
251                .build()
252                .is_err()
253        );
254        assert!(Config::builder().lna_gain_db(7).build().is_err());
255        assert!(Config::builder().vga_gain_db(3).build().is_err());
256        assert!(Config::builder().tx_vga_gain_db(48).build().is_err());
257        assert!(
258            Config::builder()
259                .lna_gain_db(40)
260                .vga_gain_db(62)
261                .build()
262                .is_ok()
263        );
264    }
265}