diff --git a/crates/arctictis/src/command/bc125at/channel_info.rs b/crates/arctictis/src/command/bc125at/channel_info.rs index ad7d773..fc99a18 100644 --- a/crates/arctictis/src/command/bc125at/channel_info.rs +++ b/crates/arctictis/src/command/bc125at/channel_info.rs @@ -2,6 +2,7 @@ use crate::{ Command, OkResponse, bc125at::common::{ChannelIndex, Delay, Lockout}, command::SingleParam, + tones::CtcssDcsStatus, }; get_set_command!( @@ -29,8 +30,7 @@ get_set_command!( /// Narrow-band frequecy modulation Nfm => b"NFM", }, - #[doc="Index of the CTCSS frequency or DCS code to use\n\nSee [the protocol spec](https://info.uniden.com/twiki/pub/UnidenMan4/BC125AT/BC125AT_PC_Protocol_V1.01.pdf) for a table"] - ctcss_dcs_status: range CtcssDcsStatus (0..=240 => usize), + ctcss_dcs_status: custom CtcssDcsStatus {}, delay: custom Delay {}, lockout: custom Lockout {}, /// Whether or not a channel is prioritized for priority scan mode diff --git a/crates/arctictis/src/lib.rs b/crates/arctictis/src/lib.rs index dec65cd..8738a5a 100644 --- a/crates/arctictis/src/lib.rs +++ b/crates/arctictis/src/lib.rs @@ -44,6 +44,7 @@ mod bytes_split; mod codec; mod command; mod scanner; +pub mod tones; pub use crate::{ command::{Command, NonProgramModeCommand, OkResponse, OkResponseError, bc125at}, diff --git a/crates/arctictis/src/tones.rs b/crates/arctictis/src/tones.rs new file mode 100644 index 0000000..5eff878 --- /dev/null +++ b/crates/arctictis/src/tones.rs @@ -0,0 +1,256 @@ +use crate::command::{IntoParam, ResponseField}; + +/// The type of tone signaling a channel uses +/// +/// See the RadioReference wiki for info on [CTCSS frequencies][ctcss] and [DCS codes][dcs]. +/// +/// [ctcss]: https://wiki.radioreference.com/index.php/Continuous_Tone-Coded_Squelch_System +/// [dcs]: https://wiki.radioreference.com/index.php/DCS +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum CtcssDcsStatus { + /// Ignore all CTCSS/DCS tones + NoneAll, + /// Open squelch on any signal, displaying any tone received + Search, + /// Only open squelch if no CTCSS or DCS tone is transmitted + NoTone, + /// Open squelch only if the CTCSS tone matches + Ctcss(CtcssFreq), + /// Open squelch only if the DCS tone matches + Dcs(Dcs), +} + +impl CtcssDcsStatus { + fn code(self) -> u8 { + match self { + Self::NoneAll => 0, + Self::Search => 127, + Self::NoTone => 240, + Self::Ctcss(ctcss_freq) => ctcss_freq.code(), + Self::Dcs(dcs) => dcs.code(), + } + } +} + +impl IntoParam for CtcssDcsStatus { + fn into_param(self) -> tokio_util::bytes::Bytes { + tokio_util::bytes::Bytes::from(format!("{}", self.code())) + } + + fn size_hint(&self) -> usize { + ::MAX_STR_LEN + } +} + +impl ResponseField for CtcssDcsStatus { + fn deserialize(raw: &[u8]) -> Option { + let code: u8 = str::from_utf8(raw).ok()?.parse().ok()?; + let status = match code { + 0 => Self::NoneAll, + 127 => Self::Search, + 240 => Self::NoTone, + 64..=113 => Self::Ctcss(CtcssFreq(code)), + 128..=231 => Self::Dcs(Dcs(code)), + _ => return None, + }; + Some(status) + } +} + +/// A [CTCSS tone frequency][ctcss] +/// +/// The tone frequency is internally mapped to a scanner-specific code. See RadioReference for a +/// list of valid frequencies. +/// +/// [ctcss]: https://wiki.radioreference.com/index.php/Continuous_Tone-Coded_Squelch_System +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CtcssFreq(u8); + +impl CtcssFreq { + pub fn new(frequency: f32) -> Option { + let code = match frequency { + 67.0 => 64, + 69.3 => 65, + 71.9 => 66, + 74.4 => 67, + 77.0 => 68, + 79.7 => 69, + 82.5 => 70, + 85.4 => 71, + 88.5 => 72, + 91.5 => 73, + 94.8 => 74, + 97.4 => 75, + 100.0 => 76, + 103.5 => 77, + 107.2 => 78, + 110.9 => 79, + 114.8 => 80, + 118.8 => 81, + 123.0 => 82, + 127.3 => 83, + 131.8 => 84, + 136.5 => 85, + 141.3 => 86, + 146.2 => 87, + 151.4 => 88, + 156.7 => 89, + 159.8 => 90, + 162.2 => 91, + 165.5 => 92, + 167.9 => 93, + 171.3 => 94, + 173.8 => 95, + 177.3 => 96, + 179.9 => 97, + 183.5 => 98, + 186.2 => 99, + 189.9 => 100, + 192.8 => 101, + 196.6 => 102, + 199.5 => 103, + 203.5 => 104, + 206.5 => 105, + 210.7 => 106, + 218.1 => 107, + 225.7 => 108, + 229.1 => 109, + 233.6 => 110, + 241.8 => 111, + 250.3 => 112, + 254.1 => 113, + _ => return None, + }; + Some(Self(code)) + } + + pub fn code(self) -> u8 { + self.0 + } +} + +/// A [DCS code][dcs] +/// +/// The DCS code is internally mapped to a scanner-specific code. See RadioReference for a +/// list of valid codes. +/// +/// [dcs]: https://wiki.radioreference.com/index.php/DCS +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Dcs(u8); + +impl Dcs { + pub fn new(code: u16) -> Option { + let code = match code { + 23 => 128, + 25 => 129, + 26 => 130, + 31 => 131, + 32 => 132, + 36 => 133, + 43 => 134, + 47 => 135, + 51 => 136, + 53 => 137, + 54 => 138, + 65 => 139, + 71 => 140, + 72 => 141, + 73 => 142, + 74 => 143, + 114 => 144, + 115 => 145, + 116 => 146, + 122 => 147, + 125 => 148, + 131 => 149, + 132 => 150, + 134 => 151, + 143 => 152, + 145 => 153, + 152 => 154, + 155 => 155, + 156 => 156, + 162 => 157, + 165 => 158, + 172 => 159, + 174 => 160, + 205 => 161, + 212 => 162, + 223 => 163, + 225 => 164, + 226 => 165, + 243 => 166, + 244 => 167, + 245 => 168, + 246 => 169, + 251 => 170, + 252 => 171, + 255 => 172, + 261 => 173, + 263 => 174, + 265 => 175, + 266 => 176, + 271 => 177, + 274 => 178, + 306 => 179, + 311 => 180, + 315 => 181, + 325 => 182, + 331 => 183, + 332 => 184, + 343 => 185, + 346 => 186, + 351 => 187, + 356 => 188, + 364 => 189, + 365 => 190, + 371 => 191, + 411 => 192, + 412 => 193, + 413 => 194, + 423 => 195, + 431 => 196, + 432 => 197, + 445 => 198, + 446 => 199, + 452 => 200, + 454 => 201, + 455 => 202, + 462 => 203, + 464 => 204, + 465 => 205, + 466 => 206, + 503 => 207, + 506 => 208, + 516 => 209, + 523 => 210, + 526 => 211, + 532 => 212, + 546 => 213, + 565 => 214, + 606 => 215, + 612 => 216, + 624 => 217, + 627 => 218, + 631 => 219, + 632 => 220, + 654 => 221, + 662 => 222, + 664 => 223, + 703 => 224, + 712 => 225, + 723 => 226, + 731 => 227, + 732 => 228, + 734 => 229, + 743 => 230, + 754 => 231, + _ => return None, + }; + Some(Self(code)) + } + + pub fn code(self) -> u8 { + self.0 + } +}