Skip to main content

hackrf_nusb/
config.rs

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