vfo-sync

Bidirectional VFO synchronization for IC-7300 and SDR++
git clone https://git.bradleygannon.com/vfo-sync.git
Log | Files | Refs

ic7300.rs (6254B)


      1 use log::{debug, trace};
      2 use serialport::{DataBits::Eight, Parity::None, StopBits::One};
      3 use std::{
      4     sync::mpsc::{Receiver, Sender},
      5     time::{Duration, Instant},
      6 };
      7 
      8 use crate::{Freq, Ptt, State};
      9 
     10 enum CivCmd {
     11     SetFreq(Freq),
     12     SetPtt(Ptt),
     13     ReadPtt,
     14 }
     15 
     16 fn to_bcd(n: u8) -> u8 {
     17     ((n / 10) * 16) + (n % 10)
     18 }
     19 
     20 fn from_bcd(n: u8) -> u8 {
     21     ((n / 16) * 10) + (n % 16)
     22 }
     23 
     24 impl From<CivCmd> for Vec<u8> {
     25     fn from(value: CivCmd) -> Self {
     26         match value {
     27             CivCmd::SetFreq(f) => {
     28                 let tens_ones_hz = (f % 100) as u8;
     29                 let thousands_hundreds_hz = ((f / 100) % 100) as u8;
     30                 let hundreds_tens_khz = ((f / 10_000) % 100) as u8;
     31                 let tens_ones_mhz = ((f / 1_000_000) % 100) as u8;
     32                 let bytes = vec![
     33                     0xfe,
     34                     0xfe,
     35                     0x94,
     36                     0xe1,
     37                     0x00,
     38                     to_bcd(tens_ones_hz),
     39                     to_bcd(thousands_hundreds_hz),
     40                     to_bcd(hundreds_tens_khz),
     41                     to_bcd(tens_ones_mhz),
     42                     0x00,
     43                     0xfd,
     44                 ];
     45                 trace!("Frequency is {}, converted to {:x?}", f, bytes);
     46                 bytes
     47             }
     48             CivCmd::SetPtt(p) => {
     49                 let bytes = vec![
     50                     0xfe,
     51                     0xfe,
     52                     0x94,
     53                     0xe1,
     54                     0x1c,
     55                     0x00,
     56                     if p { 0x01 } else { 0x00 },
     57                     0xfd,
     58                 ];
     59                 trace!("PTT is {}, converted to {:x?}", p, bytes);
     60                 bytes
     61             }
     62             CivCmd::ReadPtt => vec![0xfe, 0xfe, 0x94, 0xe1, 0x1c, 0x00, 0xfd],
     63         }
     64     }
     65 }
     66 
     67 #[derive(Debug)]
     68 enum CivMsg {
     69     Freq(Freq),
     70     Ptt(Ptt),
     71 }
     72 
     73 #[derive(Debug)]
     74 enum CivMsgError {
     75     InputTooShort,
     76     InvalidStartOrEnd,
     77     WrongAddress,
     78     WrongCommand,
     79 }
     80 
     81 impl TryFrom<&[u8]> for CivMsg {
     82     type Error = CivMsgError;
     83     fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
     84         if value.len() < 8 {
     85             trace!("Input too short: {} < 8", value.len());
     86             return Err(CivMsgError::InputTooShort);
     87         }
     88         if value[..2] != [0xfe, 0xfe] || *value.last().unwrap() != 0xfd {
     89             trace!(
     90                 "Invalid start or end: [0x{:x}, 0x{:x}, ... , 0x{:x}]",
     91                 value[0],
     92                 value[1],
     93                 value.last().unwrap()
     94             );
     95             return Err(CivMsgError::InvalidStartOrEnd);
     96         }
     97         if value[2..4] != [0xe1, 0x94] && value[2..4] != [0x00, 0x94] {
     98             trace!(
     99                 "Wrong address: [..., 0x{:x}, 0x{:x}, ...]",
    100                 value[2],
    101                 value[3]
    102             );
    103             return Err(CivMsgError::WrongAddress);
    104         }
    105         if value[4] == 0x00 && value.len() == 11 {
    106             let freq = from_bcd(value[5]) as u32
    107                 + from_bcd(value[6]) as u32 * 100
    108                 + from_bcd(value[7]) as u32 * 10_000
    109                 + from_bcd(value[8]) as u32 * 1_000_000;
    110             return Ok(CivMsg::Freq(freq));
    111         }
    112         if value[4..6] == [0x1c, 0x00] && value.len() == 8 {
    113             return Ok(CivMsg::Ptt(value[6] != 0x00));
    114         }
    115         trace!("Command doesn't apply to us");
    116         Err(CivMsgError::WrongCommand)
    117     }
    118 }
    119 
    120 pub(crate) fn ic7300_handler(tx: Sender<State>, rx: Receiver<State>) {
    121     let mut port = serialport::new("/dev/ttyUSB1", 115_200)
    122         .data_bits(Eight)
    123         .parity(None)
    124         .stop_bits(One)
    125         .open()
    126         .unwrap();
    127     debug!("Serial port set up");
    128     let mut current_state = State::default();
    129     let mut read_buf = [0u8; 1024];
    130     let mut last_ptt_check = Instant::now();
    131     loop {
    132         if let Some(new_state) = rx.try_iter().last() {
    133             trace!("Got new state from main thread");
    134             if new_state.freq != current_state.freq {
    135                 let mut freq_bytes: Vec<u8> = CivCmd::SetFreq(new_state.freq).into();
    136                 port.write_all(&mut freq_bytes).unwrap();
    137                 debug!("Set new frequency to {}", new_state.freq);
    138             }
    139             if new_state.ptt != current_state.ptt {
    140                 let mut ptt_bytes: Vec<u8> = CivCmd::SetPtt(new_state.ptt).into();
    141                 port.write_all(&mut ptt_bytes).unwrap();
    142                 debug!("Set PTT to {}", new_state.ptt);
    143             }
    144             current_state = new_state;
    145         }
    146         if last_ptt_check.elapsed() > Duration::from_millis(100) {
    147             let mut ptt_bytes: Vec<u8> = CivCmd::ReadPtt.into();
    148             port.write_all(&mut ptt_bytes).unwrap();
    149             last_ptt_check = Instant::now();
    150             trace!("Sent ReadPtt command");
    151         }
    152         let mut new_state = current_state;
    153         if port.bytes_to_read().unwrap() == 0 {
    154             // trace!("No bytes to read, continuing");
    155             continue;
    156         }
    157         let n = match port.read(&mut read_buf) {
    158             Ok(n) => n,
    159             Err(_) => return,
    160         };
    161         trace!("{:?}", read_buf[..n].to_vec());
    162         let mut idx = 0;
    163         let mut start = Option::None;
    164         let mut msgs: Vec<CivMsg> = vec![];
    165         while idx < n {
    166             // trace!("{}", idx);
    167             if read_buf[idx] == 0xfe && read_buf[idx + 1] == 0xfe {
    168                 start = Some(idx);
    169             } else if read_buf[idx] == 0xfd {
    170                 let end = idx + 1;
    171                 match read_buf[start.unwrap()..end].try_into() {
    172                     Ok(msg) => {
    173                         debug!("Got new CivMsg: {:?}", msg);
    174                         msgs.push(msg);
    175                     }
    176                     Err(e) => trace!("Got a message that we don't care about: {:?}", e),
    177                 }
    178                 start = Option::None;
    179             }
    180             idx += 1;
    181         }
    182         for msg in msgs {
    183             match msg {
    184                 CivMsg::Freq(f) => new_state.freq = f,
    185                 CivMsg::Ptt(p) => new_state.ptt = p,
    186             }
    187         }
    188         if new_state != current_state {
    189             tx.send(new_state).unwrap();
    190             debug!("Sent new state to main thread");
    191         }
    192     }
    193 }