cc1101_rust/
config.rs

1//! Configuration settings for the CC1101 radio
2//!
3//! The [`RXConfig`] and [`TXConfig`] structs are used to control the receive and transmit configuration of the CC1101.
4//!
5use crate::patable::{TX_POWERS_315, TX_POWERS_433, TX_POWERS_868, TX_POWERS_915};
6use crate::{CC1101Error, ConfigError};
7use std::fmt;
8
9/// Radio modulation mode
10#[derive(Debug, Copy, Clone, PartialEq)]
11#[repr(u8)]
12pub enum Modulation {
13    /// Frequency Shift Keying (2 Frequencies)
14    FSK2 = 0,
15    /// Gaussian Shaped Frequency Shift Keying
16    GFSK = 1,
17    /// On-Off Keying
18    OOK = 3,
19    /// Frequency Shift Keying (4 Frequencies)
20    FSK4 = 4,
21    /// Minimum Shift Keying
22    MSK = 7,
23}
24
25#[derive(Debug, Copy, Clone, PartialEq)]
26pub enum CarrierSense {
27    Relative(i8),
28    Absolute(i8),
29}
30
31impl fmt::Display for CarrierSense {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            CarrierSense::Relative(v) => write!(f, "Relative(+{} dB)", v),
35            CarrierSense::Absolute(v) => write!(f, "Absolute({} dB)", v),
36        }
37    }
38}
39
40#[derive(Debug, Copy, Clone, PartialEq)]
41#[repr(u8)]
42enum CarrierSenseMode {
43    Disabled = 0,
44    Relative = 1,
45    Absolute = 2,
46}
47
48/// Device / driver register types
49#[derive(Copy, Clone)]
50pub enum RegistersType {
51    /// Hardware registers
52    Device,
53    /// Driver transmit configuration registers
54    Tx,
55    /// Driver receive configuration registers
56    Rx,
57}
58
59/// CC1101 register values
60#[allow(non_snake_case)]
61#[repr(C, packed)]
62#[derive(Debug, Default)]
63pub struct Registers {
64    /// GDO2 Output Pin Configuration
65    pub IOCFG2: u8,
66    /// GDO1 Output Pin Configuration
67    pub IOCFG1: u8,
68    /// GDO0 Output Pin Configuration
69    pub IOCFG0: u8,
70    /// RX FIFO and TX FIFO Thresholds
71    pub FIFOTHR: u8,
72    /// Sync Word, High Byte
73    pub SYNC1: u8,
74    /// Sync Word, Low Byte
75    pub SYNC0: u8,
76    /// Packet Length
77    pub PKTLEN: u8,
78    /// Packet Automation Control
79    pub PKTCTRL1: u8,
80    /// Packet Automation Control
81    pub PKTCTRL0: u8,
82    /// Device Address
83    pub ADDR: u8,
84    /// Channel Number
85    pub CHANNR: u8,
86    /// Frequency Synthesizer Control
87    pub FSCTRL1: u8,
88    /// Frequency Synthesizer Control
89    pub FSCTRL0: u8,
90    /// Frequency Control Word, High Byte
91    pub FREQ2: u8,
92    /// Frequency Control Word, Middle Byte
93    pub FREQ1: u8,
94    /// Frequency Control Word, Low Byte
95    pub FREQ0: u8,
96    /// Modem Configuration
97    pub MDMCFG4: u8,
98    /// Modem Configuration
99    pub MDMCFG3: u8,
100    /// Modem Configuration
101    pub MDMCFG2: u8,
102    /// Modem Configuration
103    pub MDMCFG1: u8,
104    /// Modem Configuration
105    pub MDMCFG0: u8,
106    /// Modem Deviation Setting
107    pub DEVIATN: u8,
108    /// Main Radio Control State Machine Configuration
109    pub MCSM2: u8,
110    /// Main Radio Control State Machine Configuration
111    pub MCSM1: u8,
112    /// Main Radio Control State Machine Configuration
113    pub MCSM0: u8,
114    /// Frequency Offset Compensation Configuration
115    pub FOCCFG: u8,
116    /// Bit Synchronization Configuration
117    pub BSCFG: u8,
118    /// AGC Control
119    pub AGCCTRL2: u8,
120    /// AGC Control
121    pub AGCCTRL1: u8,
122    /// AGC Control
123    pub AGCCTRL0: u8,
124    /// High Byte Event0 Timeout
125    pub WOREVT1: u8,
126    /// Low Byte Event0 Timeout
127    pub WOREVT0: u8,
128    /// Wake On Radio Control
129    pub WORCTRL: u8,
130    /// Front End RX Configuration
131    pub FREND1: u8,
132    /// Front End TX Configuration
133    pub FREND0: u8,
134    /// Frequency Synthesizer Calibration
135    pub FSCAL3: u8,
136    /// Frequency Synthesizer Calibration
137    pub FSCAL2: u8,
138    /// Frequency Synthesizer Calibration
139    pub FSCAL1: u8,
140    /// Frequency Synthesizer Calibration
141    pub FSCAL0: u8,
142    /// RC Oscillator Configuration
143    pub RCCTRL1: u8,
144    /// RC Oscillator Configuration
145    pub RCCTRL0: u8,
146    /// Frequency Synthesizer Calibration Control
147    pub FSTEST: u8,
148    /// Production Test
149    pub PTEST: u8,
150    /// AGC Test
151    pub AGCTEST: u8,
152    /// Various Test Settings
153    pub TEST2: u8,
154    /// Various Test Settings
155    pub TEST1: u8,
156    /// Various Test Settings
157    pub TEST0: u8,
158}
159
160/// Configuration values shared between transmit and receive
161#[repr(C)]
162#[derive(Debug, Clone, PartialEq)]
163pub struct CommonConfig {
164    frequency: u32,
165    modulation: Modulation,
166    baud_rate_mantissa: u8,
167    baud_rate_exponent: u8,
168    deviation_mantissa: u8,
169    deviation_exponent: u8,
170    sync_word: u32,
171}
172
173impl Default for CommonConfig {
174    fn default() -> CommonConfig {
175        CommonConfig {
176            frequency: 0x10B071, // 433.92
177            modulation: Modulation::OOK,
178            baud_rate_mantissa: 0x43, // 1.0 kBaud
179            baud_rate_exponent: 0x05,
180            deviation_mantissa: 0x07, // 47.607422
181            deviation_exponent: 0x04,
182            sync_word: 0x0,
183        }
184    }
185}
186
187impl fmt::Display for CommonConfig {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        write!(f, "CommonConfig: {{Frequency: {} MHz, Modulation: {:?}, Baud Rate: {} kBaud, Deviation: {} kHz, Sync Word: 0x{:08x}}}", Self::get_frequency(self), self.modulation, Self::get_baud_rate(self), Self::get_deviation(self), self.sync_word)
190    }
191}
192
193/// Configuration values specific to receive
194#[repr(C)]
195#[derive(Debug, Clone, PartialEq)]
196pub struct RXConfig {
197    common: CommonConfig,
198    bandwidth_mantissa: u8,
199    bandwidth_exponent: u8,
200    max_lna_gain: u8,
201    max_dvga_gain: u8,
202    magn_target: u8,
203    carrier_sense_mode: CarrierSenseMode,
204    carrier_sense: i8,
205    packet_length: u32,
206}
207
208impl Default for RXConfig {
209    fn default() -> RXConfig {
210        RXConfig {
211            common: CommonConfig::default(),
212            bandwidth_mantissa: 0x00, // 203
213            bandwidth_exponent: 0x02,
214            max_lna_gain: 0,
215            max_dvga_gain: 0,
216            magn_target: 33,
217            carrier_sense_mode: CarrierSenseMode::Relative,
218            carrier_sense: 6,
219            packet_length: 1024,
220        }
221    }
222}
223
224impl fmt::Display for RXConfig {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        let carrier_sense = match Self::get_carrier_sense(self) {
227            Some(v) => format!("{}", v),
228            None => "Disabled".to_owned(),
229        };
230
231        write!(f, "RXConfig: {{{}, Bandwidth: {} kHz, Max LNA Gain: {} dB, Max DVGA Gain: {} dB, Magn Target: {} dB, Carrier Sense: {}, Packet Length: {}}}", self.common, Self::get_bandwith(self), self.max_lna_gain, self.max_dvga_gain, self.magn_target, carrier_sense, self.packet_length)
232    }
233}
234
235/// Configuration values specific to transmit
236#[repr(C)]
237#[derive(Debug, Default)]
238pub struct TXConfig {
239    common: CommonConfig,
240    tx_power: u8,
241}
242
243impl fmt::Display for TXConfig {
244    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245        let tx_power = match Self::get_tx_power(self) {
246            Ok(tx_power) => format!("{} dBm", tx_power),
247            Err(_) => format!("{:02x}", self.tx_power),
248        };
249
250        write!(f, "TXConfig: {{{}, TX Power: {}}}", self.common, tx_power)
251    }
252}
253
254const XTAL_FREQ: f32 = 26.0;
255
256fn round(value: f32, precision: u8) -> f32 {
257    let m = 10_f32.powi(precision as i32);
258    (value * m).round() / m
259}
260
261impl CommonConfig {
262    /// Create a new CommonConfig
263    ///
264    /// # Example
265    ///
266    /// ```
267    /// # use cc1101_rust::config::{CommonConfig, Modulation};
268    /// let config = CommonConfig::new(433.92, Modulation::OOK, 1.0, None, None)?;
269    /// # Ok::<(), cc1101_rust::CC1101Error>(())
270    /// ```
271    pub fn new(
272        frequency: f32,
273        modulation: Modulation,
274        baud_rate: f32,
275        deviation: Option<f32>,
276        sync_word: Option<u32>,
277    ) -> Result<CommonConfig, CC1101Error> {
278        let mut config = CommonConfig::default();
279        config.set_frequency(frequency)?;
280        config.set_modulation_and_baud_rate(modulation, baud_rate)?;
281
282        if let Some(sync_word) = sync_word {
283            config.set_sync_word(sync_word)?;
284        } else {
285            config.set_sync_word(0x00)?;
286        }
287
288        if let Some(deviation) = deviation {
289            config.set_deviation(deviation)?;
290        }
291
292        Ok(config)
293    }
294
295    /// Convert a frequency in MHz to a configuration value
296    /// Uses the formula from section 21 of the CC1101 datasheet
297    fn frequency_to_config(frequency: f32) -> Result<u32, CC1101Error> {
298        if !((299.99976..=347.99994).contains(&frequency)
299            || (386.99994..=463.9998).contains(&frequency)
300            || (778.9999..=928.000000).contains(&frequency))
301        {
302            return Err(CC1101Error::Config(ConfigError::InvalidFrequency));
303        }
304
305        let f = ((frequency * 65536_f32) / XTAL_FREQ) as u32;
306        Ok(f)
307    }
308
309    /// Convert a configuration value to a frequency in MHz
310    /// Uses the formula from section 21 of the CC1101 datasheet
311    fn config_to_frequency(config: u32) -> f32 {
312        (XTAL_FREQ / 2.0_f32.powi(16)) * config as f32
313    }
314
315    /// The frequency to receive/transmit on.
316    ///
317    /// Valid values are 300-348, 387-464 and 779-928 MHz.
318    pub fn set_frequency(&mut self, frequency: f32) -> Result<(), CC1101Error> {
319        self.frequency = CommonConfig::frequency_to_config(frequency)?;
320        Ok(())
321    }
322
323    /// Get the current receive/transmit frequency
324    pub fn get_frequency(&self) -> f32 {
325        CommonConfig::config_to_frequency(self.frequency)
326    }
327
328    /// Convert a baud rate in kBaud to a configuration value.
329    ///
330    /// Uses the formula from section 12 of the datasheet
331    fn baud_rate_to_config(
332        modulation: Modulation,
333        baud_rate: f32,
334    ) -> Result<(u8, u8), CC1101Error> {
335        let valid_baud_rate = match modulation {
336            Modulation::GFSK | Modulation::OOK => (0.599742..=249.939).contains(&baud_rate),
337            Modulation::FSK2 => (0.599742..=500.0).contains(&baud_rate),
338            Modulation::FSK4 => (0.599742..=299.927).contains(&baud_rate),
339            Modulation::MSK => (25.9857..=499.878).contains(&baud_rate),
340        };
341
342        if !valid_baud_rate {
343            return Err(CC1101Error::Config(ConfigError::InvalidBaudRate));
344        }
345
346        let xtal_freq = XTAL_FREQ * 1000000.0;
347
348        let r_data = baud_rate * 1000.0;
349
350        let exponent = ((r_data * 2_f32.powi(20)) / xtal_freq).log(2.0).floor();
351        let mantissa =
352            ((r_data * 2_f32.powi(28) / (xtal_freq * 2_f32.powf(exponent))) - 256_f32).round();
353
354        let mantissa = mantissa as u8;
355        let exponent = exponent as u8;
356
357        Ok((mantissa, exponent))
358    }
359
360    /// Convert a baud rate configuration value to kBaud
361    fn config_to_baud_rate(mantissa: u8, exponent: u8) -> f32 {
362        let xtal_freq = XTAL_FREQ * 1000000.0;
363
364        let r_data = ((((256 + mantissa as u32) as f32) * 2_f32.powi(exponent as i32))
365            / 2_f32.powi(28))
366            * xtal_freq;
367
368        round(r_data / 1000.0, 6)
369    }
370
371    /// Set the modulation scheme and the baud rate in kBaud
372    ///
373    /// # Valid Modulation / Baud Rate Values
374    ///
375    /// | Modulation           | Baud Rate  |
376    /// | ---------------------| ---------- |
377    /// | [`Modulation::OOK`]  | 0.6 - 250  |
378    /// | [`Modulation::GFSK`] | 0.6 - 250  |
379    /// | [`Modulation::FSK2`] | 0.6 - 500  |
380    /// | [`Modulation::FSK4`] | 0.6 - 300  |
381    /// | [`Modulation::MSK`]  | 26 - 500   |
382    ///
383    pub fn set_modulation_and_baud_rate(
384        &mut self,
385        modulation: Modulation,
386        baud_rate: f32,
387    ) -> Result<(), CC1101Error> {
388        let (mantissa, exponent) = CommonConfig::baud_rate_to_config(modulation, baud_rate)?;
389        self.modulation = modulation;
390        self.baud_rate_mantissa = mantissa;
391        self.baud_rate_exponent = exponent;
392        Ok(())
393    }
394
395    /// Get the current modulation
396    pub fn get_modulation(&self) -> Modulation {
397        self.modulation
398    }
399
400    /// Get the current baud rate in kBaud
401    pub fn get_baud_rate(&self) -> f32 {
402        CommonConfig::config_to_baud_rate(self.baud_rate_mantissa, self.baud_rate_exponent)
403    }
404
405    /// Convert a deviation configuration value to kHz
406    ///
407    /// Uses the formula from section 16.1 of the datasheet
408    fn config_to_deviation(mantissa: u8, exponent: u8) -> f32 {
409        let xtal_freq = XTAL_FREQ * 1000000.0;
410        let dev =
411            (xtal_freq / 2_f32.powi(17)) * (mantissa + 8) as f32 * 2_f32.powi(exponent as i32);
412        round(dev / 1000.0, 6)
413    }
414
415    /// Convert a deviation in kHz to a configuration value
416    fn deviation_to_config(deviation: f32) -> Result<(u8, u8), CC1101Error> {
417        for mantissa in 0..8 {
418            for exponent in 0..8 {
419                #[allow(clippy::float_cmp)]
420                if CommonConfig::config_to_deviation(mantissa, exponent) == deviation {
421                    return Ok((mantissa, exponent));
422                }
423            }
424        }
425        Err(CC1101Error::Config(ConfigError::InvalidDeviation))
426    }
427
428    /// Set the frequency deviation in kHz
429    pub fn set_deviation(&mut self, deviation: f32) -> Result<(), CC1101Error> {
430        let (mantissa, exponent) = CommonConfig::deviation_to_config(deviation)?;
431        self.deviation_mantissa = mantissa;
432        self.deviation_exponent = exponent;
433        Ok(())
434    }
435
436    /// Get the frequency deviation in kHz
437    pub fn get_deviation(&self) -> f32 {
438        CommonConfig::config_to_deviation(self.deviation_mantissa, self.deviation_exponent)
439    }
440
441    /// Convert a sync word to a configuration value.
442    fn sync_word_to_config(sync_word: u32) -> Result<u32, CC1101Error> {
443        if sync_word > 0xFFFF {
444            let lsb = sync_word & 0x0000FFFF;
445            let msb = sync_word >> 16;
446
447            if lsb != msb {
448                return Err(CC1101Error::Config(ConfigError::InvalidSyncWord));
449            }
450        }
451        Ok(sync_word)
452    }
453
454    /// Set the sync word
455    ///
456    /// Any sync word between 0x0000 and 0xFFFF is allowed. Above 0xFFFF, the high and low 16-bits must be the same (e.g `0x0f0f0f0f`).
457    ///
458    /// In RX, the device searches for the specified sync word to begin reception.
459    ///
460    /// In TX, the sync word is prepended to each packet.
461    pub fn set_sync_word(&mut self, sync_word: u32) -> Result<(), CC1101Error> {
462        self.sync_word = CommonConfig::sync_word_to_config(sync_word)?;
463        Ok(())
464    }
465
466    /// Get the configured sync word
467    pub fn get_sync_word(&self) -> u32 {
468        self.sync_word
469    }
470}
471
472impl RXConfig {
473    /// Create a new receive configuration
474    ///
475    /// See [`CommonConfig`] for valid argument values.
476    ///
477    /// # Example
478    ///
479    /// ```
480    /// # use cc1101_rust::config::{RXConfig, Modulation};
481    /// let config = RXConfig::new(433.92, Modulation::OOK, 1.0, 1024, None, None, None, None, None, None, None)?;
482    /// # Ok::<(), cc1101_rust::CC1101Error>(())
483    /// ```
484    #[allow(clippy::too_many_arguments)]
485    pub fn new(
486        frequency: f32,
487        modulation: Modulation,
488        baud_rate: f32,
489        packet_length: u32,
490        deviation: Option<f32>,
491        sync_word: Option<u32>,
492        bandwidth: Option<u32>,
493        carrier_sense: Option<CarrierSense>,
494        max_lna_gain: Option<u8>,
495        max_dvga_gain: Option<u8>,
496        magn_target: Option<u8>,
497    ) -> Result<RXConfig, CC1101Error> {
498        let common = CommonConfig::new(frequency, modulation, baud_rate, deviation, sync_word)?;
499
500        let mut rx_config = RXConfig {
501            common,
502            packet_length,
503            ..RXConfig::default()
504        };
505
506        rx_config.set_carrier_sense(carrier_sense)?;
507
508        if let Some(bandwidth) = bandwidth {
509            rx_config.set_bandwidth(bandwidth)?;
510        }
511
512        if let Some(max_lna_gain) = max_lna_gain {
513            rx_config.set_max_lna_gain(max_lna_gain)?;
514        }
515
516        if let Some(max_dvga_gain) = max_dvga_gain {
517            rx_config.set_max_dvga_gain(max_dvga_gain)?;
518        }
519
520        if let Some(magn_target) = magn_target {
521            rx_config.set_magn_target(magn_target)?;
522        }
523
524        Ok(rx_config)
525    }
526
527    /// Get the common configuration elements
528    pub fn get_common_config(&self) -> &CommonConfig {
529        &self.common
530    }
531
532    /// Get a mutable reference to the common configuration elements
533    pub fn get_common_config_mut(&mut self) -> &mut CommonConfig {
534        &mut self.common
535    }
536
537    /// Convert a bandwidth configuration value to kHz.
538    ///
539    /// Uses the formula from section 13 of the datasheet
540    fn config_to_bandwidth(mantissa: u8, exponent: u8) -> u32 {
541        let xtal_freq = XTAL_FREQ * 1000000.0;
542        let bw_channel = xtal_freq / (8.0 * (mantissa as f32 + 4.0) * 2_f32.powi(exponent as i32));
543        (bw_channel / 1000.0) as u32
544    }
545
546    /// Convert a bandwidth in kHz to a configuration value
547    fn bandwidth_to_config(bandwidth: u32) -> Result<(u8, u8), CC1101Error> {
548        for mantissa in 0..4 {
549            for exponent in 0..4 {
550                #[allow(clippy::float_cmp)]
551                if bandwidth == RXConfig::config_to_bandwidth(mantissa, exponent) {
552                    return Ok((mantissa, exponent));
553                }
554            }
555        }
556        Err(CC1101Error::Config(ConfigError::InvalidBandwidth))
557    }
558
559    /// Set the configured bandwith in KHz
560    ///
561    /// Valid values are `58,67,81,101,116,135,162,203,232,270,325,406,464,541,650,812`
562    pub fn set_bandwidth(&mut self, bandwidth: u32) -> Result<(), CC1101Error> {
563        let (mantissa, exponent) = RXConfig::bandwidth_to_config(bandwidth)?;
564        self.bandwidth_mantissa = mantissa;
565        self.bandwidth_exponent = exponent;
566        Ok(())
567    }
568
569    /// Get the configured bandwidth
570    pub fn get_bandwith(&self) -> u32 {
571        RXConfig::config_to_bandwidth(self.bandwidth_mantissa, self.bandwidth_exponent)
572    }
573
574    /// Sets the carrier sense threshold in dB.
575    ///
576    /// For [`CarrierSense::Relative`] an increase of 6, 10 or 14 dB can be specified. This will begin RX on a sudden increase in RSSI greather than or equal to this value.
577    ///
578    /// For [`CarrierSense::Absolute`] a value between -7 dB and 7 dB can be set. When RSSI exceeds `magn_target` +/- this value, packet RX will begin.
579    /// `max_lna_gain` and `max_dvga_gain` will also require configuring to set the correct absolute RSSI value.
580    ///
581    /// [`None`] disables carrier sense.
582    pub fn set_carrier_sense(
583        &mut self,
584        carrier_sense: Option<CarrierSense>,
585    ) -> Result<(), CC1101Error> {
586        match carrier_sense {
587            Some(CarrierSense::Relative(carrier_sense)) => match carrier_sense {
588                6 | 10 | 14 => {
589                    self.carrier_sense_mode = CarrierSenseMode::Relative;
590                    self.carrier_sense = carrier_sense;
591                }
592                _ => return Err(CC1101Error::Config(ConfigError::InvalidCarrierSense)),
593            },
594            Some(CarrierSense::Absolute(carrier_sense)) => match carrier_sense {
595                -7..=7 => {
596                    self.carrier_sense_mode = CarrierSenseMode::Absolute;
597                    self.carrier_sense = carrier_sense;
598                }
599                _ => return Err(CC1101Error::Config(ConfigError::InvalidCarrierSense)),
600            },
601            None => {
602                self.carrier_sense_mode = CarrierSenseMode::Disabled;
603                self.carrier_sense = 0;
604            }
605        }
606        Ok(())
607    }
608
609    /// Get the configured carrier sense
610    pub fn get_carrier_sense(&self) -> Option<CarrierSense> {
611        match self.carrier_sense_mode {
612            CarrierSenseMode::Disabled => None,
613            CarrierSenseMode::Relative => Some(CarrierSense::Relative(self.carrier_sense)),
614            CarrierSenseMode::Absolute => Some(CarrierSense::Absolute(self.carrier_sense)),
615        }
616    }
617
618    /// Sets the amount to decrease the maximum LNA gain by approximately the specified amount in dB.
619    /// Valid values are `0, 3, 6, 7, 9, 12, 15, 17`
620    pub fn set_max_lna_gain(&mut self, max_lna_gain: u8) -> Result<(), CC1101Error> {
621        match max_lna_gain {
622            0 | 3 | 6 | 7 | 9 | 12 | 15 | 17 => self.max_lna_gain = max_lna_gain,
623            _ => return Err(CC1101Error::Config(ConfigError::InvalidMaxLNAGain)),
624        }
625        Ok(())
626    }
627
628    /// Get the configured maximum LNA gain
629    pub fn get_max_lna_gain(&self) -> u8 {
630        self.max_lna_gain
631    }
632
633    /// Sets the amount to decrease the maximum DVGA gain by approximately the specified amount in dB.
634    /// Valid values are `0, 6, 12, 18`
635    pub fn set_max_dvga_gain(&mut self, max_dvga_gain: u8) -> Result<(), CC1101Error> {
636        match max_dvga_gain {
637            0 | 6 | 12 | 18 => self.max_dvga_gain = max_dvga_gain,
638            _ => return Err(CC1101Error::Config(ConfigError::InvalidMaxDVGAGain)),
639        }
640        Ok(())
641    }
642
643    /// Get the configured maximum DVGA gain
644    pub fn get_max_dvga_gain(&self) -> u8 {
645        self.max_dvga_gain
646    }
647
648    /// Sets the target channel filter amplitude in dB
649    /// Valid values are `24, 27, 30, 33, 36, 38, 40, 42`
650    pub fn set_magn_target(&mut self, magn_target: u8) -> Result<(), CC1101Error> {
651        match magn_target {
652            24 | 27 | 30 | 33 | 36 | 38 | 40 | 42 => self.magn_target = magn_target,
653            _ => return Err(CC1101Error::Config(ConfigError::InvalidMagnTarget)),
654        }
655        Ok(())
656    }
657
658    /// Get the configured maximum DVGA gain
659    pub fn get_magn_target(&self) -> u8 {
660        self.magn_target
661    }
662
663    /// Set the length of packets to receive in bytes
664    pub fn set_packet_length(&mut self, packet_length: u32) {
665        self.packet_length = packet_length
666    }
667
668    /// Get the configured packet length
669    pub fn get_packet_length(&self) -> u32 {
670        self.packet_length
671    }
672}
673
674impl TXConfig {
675    /// Is a frequency close to a target frequency
676    fn frequency_near(frequency: f32, target_frequency: f32) -> bool {
677        (frequency - target_frequency).abs() < 1.0
678    }
679
680    /// Get the appropriate power table based on the provided frequency
681    fn get_power_table(frequency: f32) -> Result<&'static [(u8, f32)], CC1101Error> {
682        if Self::frequency_near(frequency, 315.0) {
683            Ok(TX_POWERS_315)
684        } else if Self::frequency_near(frequency, 433.0) {
685            Ok(TX_POWERS_433)
686        } else if Self::frequency_near(frequency, 868.0) {
687            Ok(TX_POWERS_868)
688        } else if Self::frequency_near(frequency, 915.0) {
689            Ok(TX_POWERS_915)
690        } else {
691            Err(CC1101Error::Config(ConfigError::InvalidFrequency))
692        }
693    }
694
695    /// Create a new transmit configuration
696    ///
697    /// See [`CommonConfig`] for valid argument values.
698    ///
699    /// TX power is specified in dBm. Valid values can be found in [TI DN013](https://www.ti.com/lit/an/swra151a/swra151a.pdf)
700    ///
701    /// Frequency must be close to 315/433/868/915Mhz
702    ///
703    /// # Example
704    ///
705    /// ```
706    /// # use cc1101_rust::config::{TXConfig, Modulation};
707    /// let config = TXConfig::new(433.92, Modulation::OOK, 1.0, 0.1, None, None)?;
708    /// # Ok::<(), cc1101_rust::CC1101Error>(())
709    /// ```
710    pub fn new(
711        frequency: f32,
712        modulation: Modulation,
713        baud_rate: f32,
714        tx_power: f32,
715        deviation: Option<f32>,
716        sync_word: Option<u32>,
717    ) -> Result<TXConfig, CC1101Error> {
718        let common = CommonConfig::new(frequency, modulation, baud_rate, deviation, sync_word)?;
719
720        let mut tx_config = TXConfig {
721            common,
722            ..TXConfig::default()
723        };
724
725        tx_config.set_tx_power(tx_power)?;
726
727        Ok(tx_config)
728    }
729
730    /// Get the common configuration elements
731    pub fn get_common_config(&self) -> &CommonConfig {
732        &self.common
733    }
734
735    /// Get a mutable reference to the common configuration elements
736    pub fn get_common_config_mut(&mut self) -> &mut CommonConfig {
737        &mut self.common
738    }
739
740    /// Create a new transmit configuration using a TX power specified as a raw CC1101 PATABLE byte.
741    ///
742    /// Frequency can be any valid value.
743    ///
744    /// See [`CommonConfig`] for valid argument values.
745    ///
746    /// # Example
747    ///
748    /// ```
749    /// # use cc1101_rust::config::{TXConfig, Modulation};
750    /// let mut config = TXConfig::new_raw(433.92, Modulation::OOK, 1.0, 0x60, None, None)?;
751    /// # Ok::<(), cc1101_rust::CC1101Error>(())
752    /// ```
753    pub fn new_raw(
754        frequency: f32,
755        modulation: Modulation,
756        baud_rate: f32,
757        tx_power: u8,
758        deviation: Option<f32>,
759        sync_word: Option<u32>,
760    ) -> Result<TXConfig, CC1101Error> {
761        let common = CommonConfig::new(frequency, modulation, baud_rate, deviation, sync_word)?;
762        Ok(TXConfig { common, tx_power })
763    }
764
765    /// Lookup a TX power in dBM in the appropriate power table (based on [TI DN013](https://www.ti.com/lit/an/swra151a/swra151a.pdf)).
766    ///
767    /// Frequency must be within 1MHz of 315/433/868/915Mhz
768    fn tx_power_to_config(frequency: f32, tx_power: f32) -> Result<u8, CC1101Error> {
769        let power_table = Self::get_power_table(frequency)?;
770
771        for (hex, dbm) in power_table {
772            if (dbm - tx_power).abs() < f32::EPSILON {
773                return Ok(*hex);
774            }
775        }
776
777        Err(CC1101Error::Config(ConfigError::InvalidTXPower))
778    }
779
780    /// Lookup a TX power PATABLE byte in the appropriate power table (based on [TI DN013](https://www.ti.com/lit/an/swra151a/swra151a.pdf)).
781    ///
782    /// Frequency must be within 1Mhz of 315/433/868/915Mhz
783    fn config_to_tx_power(frequency: f32, tx_power: u8) -> Result<f32, CC1101Error> {
784        let power_table = Self::get_power_table(frequency)?;
785
786        for (hex, dbm) in power_table {
787            if *hex == tx_power {
788                return Ok(*dbm);
789            }
790        }
791
792        Err(CC1101Error::Config(ConfigError::InvalidTXPower))
793    }
794
795    /// Set the TX power to a value in dBm.
796    ///
797    /// Configured frequency must be within 1Mhz of 315/433/868/915Mhz
798    pub fn set_tx_power(&mut self, tx_power: f32) -> Result<(), CC1101Error> {
799        self.tx_power = Self::tx_power_to_config(self.common.get_frequency(), tx_power)?;
800        Ok(())
801    }
802
803    /// Get the TX power in dBm.
804    ///
805    /// Configured frequency must be within 1MHz of 315/433/868/915Mhz
806    pub fn get_tx_power(&self) -> Result<f32, CC1101Error> {
807        Self::config_to_tx_power(self.common.get_frequency(), self.tx_power)
808    }
809
810    /// Set the TX power to a raw value which will be set in the devices PATABLE
811    pub fn set_tx_power_raw(&mut self, tx_power: u8) {
812        self.tx_power = tx_power;
813    }
814
815    /// Get the TX power as raw value from the devices PATABLE
816    pub fn get_tx_power_raw(&self) -> u8 {
817        self.tx_power
818    }
819}
820
821#[cfg(test)]
822mod tests {
823    #![allow(clippy::excessive_precision)]
824    use super::*;
825
826    #[test]
827    fn test_freq() -> Result<(), CC1101Error> {
828        assert_eq!(CommonConfig::frequency_to_config(315.0)?, 0x000C1D89);
829        assert_eq!(CommonConfig::frequency_to_config(433.0)?, 0x0010A762);
830        assert_eq!(CommonConfig::frequency_to_config(868.0)?, 0x00216276);
831        assert_eq!(CommonConfig::frequency_to_config(915.0)?, 0x0023313B);
832
833        assert_eq!(CommonConfig::frequency_to_config(299.999756)?, 0x000B89D8);
834        assert_eq!(CommonConfig::frequency_to_config(347.999939)?, 0x000D6276);
835        assert_eq!(CommonConfig::frequency_to_config(386.999939)?, 0x000EE276);
836        assert_eq!(CommonConfig::frequency_to_config(463.999786)?, 0x0011D89D);
837        assert_eq!(CommonConfig::frequency_to_config(778.999878)?, 0x001DF627);
838        assert_eq!(CommonConfig::frequency_to_config(928.000000)?, 0x0023B13B);
839
840        assert_eq!(CommonConfig::config_to_frequency(0x000B89D8), 299.999756);
841        assert_eq!(CommonConfig::config_to_frequency(0x000D6276), 347.999939);
842        assert_eq!(CommonConfig::config_to_frequency(0x000EE276), 386.999939);
843        assert_eq!(CommonConfig::config_to_frequency(0x0011D89D), 463.999786);
844        assert_eq!(CommonConfig::config_to_frequency(0x001DF627), 778.999878);
845        assert_eq!(CommonConfig::config_to_frequency(0x0023B13B), 928.000000);
846
847        assert_eq!(CommonConfig::config_to_frequency(0x000C1D89), 314.999664);
848        assert_eq!(CommonConfig::config_to_frequency(0x0010A762), 432.999817);
849        assert_eq!(CommonConfig::config_to_frequency(0x00216276), 867.999939);
850        assert_eq!(CommonConfig::config_to_frequency(0x0023313B), 915.000000);
851
852        assert!(CommonConfig::frequency_to_config(0.0).is_err());
853        assert!(CommonConfig::frequency_to_config(464.0).is_err());
854        assert!(CommonConfig::frequency_to_config(999.0).is_err());
855
856        Ok(())
857    }
858
859    #[test]
860    fn test_baud_rate() -> Result<(), CC1101Error> {
861        assert_eq!(
862            CommonConfig::baud_rate_to_config(Modulation::FSK2, 0.6)?,
863            (0x83, 0x04)
864        );
865        assert_eq!(
866            CommonConfig::baud_rate_to_config(Modulation::FSK2, 0.599742)?,
867            (0x83, 0x04)
868        );
869
870        assert_eq!(
871            CommonConfig::baud_rate_to_config(Modulation::FSK2, 26.0)?,
872            (0x06, 0x0A)
873        );
874        assert_eq!(
875            CommonConfig::baud_rate_to_config(Modulation::FSK2, 25.9857)?,
876            (0x06, 0x0A)
877        );
878
879        assert_eq!(
880            CommonConfig::baud_rate_to_config(Modulation::FSK2, 250.0)?,
881            (0x3B, 0x0D)
882        );
883        assert_eq!(
884            CommonConfig::baud_rate_to_config(Modulation::FSK2, 249.939)?,
885            (0x3B, 0x0D)
886        );
887
888        assert_eq!(
889            CommonConfig::baud_rate_to_config(Modulation::FSK2, 300.0)?,
890            (0x7A, 0x0D)
891        );
892        assert_eq!(
893            CommonConfig::baud_rate_to_config(Modulation::FSK2, 299.927)?,
894            (0x7A, 0x0D)
895        );
896
897        assert_eq!(
898            CommonConfig::baud_rate_to_config(Modulation::FSK2, 500.0)?,
899            (0x3B, 0x0E)
900        );
901        assert_eq!(
902            CommonConfig::baud_rate_to_config(Modulation::FSK2, 499.878)?,
903            (0x3B, 0x0E)
904        );
905
906        assert_eq!(
907            CommonConfig::baud_rate_to_config(Modulation::FSK2, 115.051)?,
908            (0x22, 0x0C)
909        );
910
911        assert_eq!(CommonConfig::config_to_baud_rate(0x83, 0x04), 0.599742);
912        assert_eq!(CommonConfig::config_to_baud_rate(0x06, 0x0A), 25.98572);
913        assert_eq!(CommonConfig::config_to_baud_rate(0x3B, 0x0D), 249.93896);
914        assert_eq!(CommonConfig::config_to_baud_rate(0x7A, 0x0D), 299.92676);
915        assert_eq!(CommonConfig::config_to_baud_rate(0x3B, 0x0E), 499.87793);
916        assert_eq!(CommonConfig::config_to_baud_rate(0x22, 0x0C), 115.05126);
917
918        assert!(CommonConfig::baud_rate_to_config(Modulation::FSK2, 0.0).is_err());
919        assert!(CommonConfig::baud_rate_to_config(Modulation::FSK2, 999.0).is_err());
920
921        Ok(())
922    }
923
924    #[test]
925    fn test_deviation() -> Result<(), CC1101Error> {
926        assert_eq!(CommonConfig::deviation_to_config(1.586914)?, (0x00, 0x00));
927        assert_eq!(CommonConfig::deviation_to_config(380.85938)?, (0x07, 0x07));
928        assert_eq!(CommonConfig::config_to_deviation(0x00, 0x00), 1.586914);
929        assert_eq!(CommonConfig::config_to_deviation(0x07, 0x07), 380.859375);
930        assert!(CommonConfig::deviation_to_config(0.0).is_err());
931        assert!(CommonConfig::deviation_to_config(400.0).is_err());
932
933        Ok(())
934    }
935
936    #[test]
937    fn test_sync_word() -> Result<(), CC1101Error> {
938        CommonConfig::sync_word_to_config(0x00000000)?;
939        CommonConfig::sync_word_to_config(0x0000FFFF)?;
940        CommonConfig::sync_word_to_config(0xFFFFFFFF)?;
941
942        assert!(CommonConfig::sync_word_to_config(0xFFFF0000).is_err());
943        assert!(CommonConfig::sync_word_to_config(0xAAAABBBB).is_err());
944        Ok(())
945    }
946
947    #[test]
948    fn test_bandwidth() -> Result<(), CC1101Error> {
949        assert_eq!(RXConfig::bandwidth_to_config(812)?, (0x00, 0x00));
950        assert_eq!(RXConfig::bandwidth_to_config(58)?, (0x03, 0x03));
951
952        assert_eq!(RXConfig::config_to_bandwidth(0x00, 0x00), 812);
953        assert_eq!(RXConfig::config_to_bandwidth(0x03, 0x03), 58);
954
955        assert!(RXConfig::bandwidth_to_config(0).is_err());
956        assert!(RXConfig::bandwidth_to_config(400).is_err());
957
958        Ok(())
959    }
960
961    #[test]
962    fn test_tx_power() -> Result<(), CC1101Error> {
963        assert!(TXConfig::config_to_tx_power(123.0, 0xFF).is_err());
964        assert!(TXConfig::config_to_tx_power(433.0, 0xFF).is_err());
965        assert!(TXConfig::tx_power_to_config(433.0, -1.0).is_err());
966
967        for frequency in [315.0, 433.0, 868.0, 915.0] {
968            let power_table = TXConfig::get_power_table(frequency)?;
969            for (hex, dbm) in power_table {
970                assert_eq!(TXConfig::config_to_tx_power(frequency, *hex)?, *dbm);
971                assert_eq!(TXConfig::tx_power_to_config(frequency, *dbm)?, *hex);
972            }
973        }
974
975        Ok(())
976    }
977}