1use 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#[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 pub fn builder() -> ConfigBuilder {
37 ConfigBuilder::default()
38 }
39
40 pub const fn frequency_hz(&self) -> u64 {
42 self.frequency_hz
43 }
44
45 pub const fn sample_rate_hz(&self) -> u32 {
47 self.sample_rate_hz
48 }
49
50 pub const fn lna_gain_db(&self) -> u8 {
52 self.lna_gain_db
53 }
54
55 pub const fn vga_gain_db(&self) -> u8 {
57 self.vga_gain_db
58 }
59
60 pub const fn amp_enabled(&self) -> bool {
62 self.amp_enabled
63 }
64
65 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#[derive(Clone, Debug, Default)]
97pub struct ConfigBuilder {
98 config: Config,
99}
100
101impl ConfigBuilder {
102 pub fn frequency_hz(mut self, value: u64) -> Self {
104 self.config.frequency_hz = value;
105 self
106 }
107
108 pub fn sample_rate_hz(mut self, value: u32) -> Self {
110 self.config.sample_rate_hz = value;
111 self
112 }
113
114 pub fn lna_gain_db(mut self, value: u8) -> Self {
116 self.config.lna_gain_db = value;
117 self
118 }
119
120 pub fn vga_gain_db(mut self, value: u8) -> Self {
122 self.config.vga_gain_db = value;
123 self
124 }
125
126 pub fn amp_enable(mut self, enabled: bool) -> Self {
128 self.config.amp_enabled = enabled;
129 self
130 }
131
132 pub fn bias_tee(mut self, enabled: bool) -> Self {
134 self.config.bias_tee_enabled = enabled;
135 self
136 }
137
138 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}