diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2023-01-01 17:33:01 +0100 | 
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2023-01-01 17:33:01 +0100 | 
| commit | adf283e4bfc50e49e92d369853eb3253481b8fbb (patch) | |
| tree | 88a0df7b6467ee66a7aa11a7b52d2f086fabc51b | |
| parent | c08d47f914da5a0f9b505dee72c4a3a6969d878d (diff) | |
| download | fl2k_ampliphase-adf283e4bfc50e49e92d369853eb3253481b8fbb.tar.gz fl2k_ampliphase-adf283e4bfc50e49e92d369853eb3253481b8fbb.tar.bz2 fl2k_ampliphase-adf283e4bfc50e49e92d369853eb3253481b8fbb.zip  | |
Add fl2k send code
| -rw-r--r-- | src/fl2k.rs | 78 | ||||
| -rw-r--r-- | src/main.rs | 87 | 
2 files changed, 123 insertions, 42 deletions
diff --git a/src/fl2k.rs b/src/fl2k.rs index 8bfc0ec..f2e197b 100644 --- a/src/fl2k.rs +++ b/src/fl2k.rs @@ -1,5 +1,5 @@ -use std::ffi::c_int; -use fl2k_ampliphase::{fl2k_dev_t, fl2k_get_device_count, fl2k_open, fl2k_close, fl2k_stop_tx, fl2k_set_sample_rate, fl2k_get_sample_rate, fl2k_tx_cb_t, fl2k_start_tx}; +use std::{ffi::{c_int, c_void}, sync::mpsc}; +use fl2k_ampliphase::{fl2k_dev_t, fl2k_get_device_count, fl2k_open, fl2k_close, fl2k_stop_tx, fl2k_set_sample_rate, fl2k_get_sample_rate, fl2k_start_tx};  #[derive(Debug)]  pub enum FL2KError { @@ -12,7 +12,7 @@ pub enum FL2KError {      Unknown(c_int)  } -fn handle_return_value(val : c_int) -> Result<(), FL2KError> { +fn handle_return_value(val: c_int) -> Result<(), FL2KError> {      if val == fl2k_ampliphase::fl2k_error_FL2K_SUCCESS { Ok(()) }      else if val == fl2k_ampliphase::fl2k_error_FL2K_TRUE { Ok(()) }      else if val == fl2k_ampliphase::fl2k_error_FL2K_ERROR_INVALID_PARAM { Err(FL2KError::InvalidParam) } @@ -29,20 +29,71 @@ pub fn get_device_count() -> u32 {  }  pub struct FL2K { -    device : *mut fl2k_dev_t, +    device: *mut fl2k_dev_t, +    callback_ctx: Box<CallbackCtx>, +    tx : mpsc::SyncSender<(Vec<i8>, Vec<i8>)>, +} + +pub struct CallbackCtx { +    // All three buffers must have same length! +    r_buf: Vec<i8>, +    g_buf: Vec<i8>, + +    rg_bufs_channel: mpsc::Receiver<(Vec<i8>, Vec<i8>)>, + +    underflow_count: u32, +    buffer_underflows: u32, + +    abort: bool, +} + +extern "C" fn tx_callback(data_info: *mut fl2k_ampliphase::fl2k_data_info_t) { +    unsafe { +        let ctx = (*data_info).ctx as *mut CallbackCtx; + +        if (*data_info).device_error != 0 { +            (*ctx).abort = true; +        } +        else { +            (*ctx).underflow_count = (*data_info).underflow_cnt; + +            match (*ctx).rg_bufs_channel.try_recv() { +                Ok((r, g)) => { +                    (*data_info).sampletype_signed = 1; + +                    (*ctx).r_buf = r; +                    (*ctx).g_buf = g; + +                    (*data_info).r_buf = (*ctx).r_buf.as_mut_ptr(); +                    (*data_info).g_buf = (*ctx).g_buf.as_mut_ptr(); +                }, +                Err(_) => { +                    (*ctx).buffer_underflows += 1; +                }, +            } +        } +    }  }  impl FL2K { -    pub fn open(device_index : u32) -> Result<Self, FL2KError> { +    pub fn open(device_index: u32) -> Result<Self, FL2KError> { +        let (tx, rx) = mpsc::sync_channel(2); +        let ctx = CallbackCtx { +            r_buf: Vec::new(), +            g_buf: Vec::new(), +            rg_bufs_channel: rx, +            underflow_count: 0, +            buffer_underflows: 0, +            abort: false, +        };          unsafe { -            let mut fl2k = FL2K { device: std::mem::zeroed() }; +            let mut fl2k = FL2K { device: std::mem::zeroed(), callback_ctx: Box::new(ctx), tx };              handle_return_value(fl2k_open(&mut fl2k.device, device_index))?;              Ok(fl2k)          }      } - -    pub fn set_sample_rate(&mut self, sample_rate : u32) -> Result<(), FL2KError> { +    pub fn set_sample_rate(&mut self, sample_rate: u32) -> Result<(), FL2KError> {          handle_return_value( unsafe { fl2k_set_sample_rate(self.device, sample_rate) })      } @@ -51,14 +102,21 @@ impl FL2K {          if sr == 0 { Err(FL2KError::Unknown(0)) } else { Ok(sr) }      } -    pub fn start_tx(&mut self, callback : fl2k_tx_cb_t, buf_num: u32) -> Result<(), FL2KError> { -        let r = unsafe { fl2k_start_tx(self.device, callback, std::ptr::null_mut(), 0) }; +    pub fn start_tx(&mut self) -> Result<(), FL2KError> { +        let r = unsafe { fl2k_start_tx(self.device, Some(tx_callback), self.callback_ctx.as_mut() as *mut CallbackCtx as *mut c_void, 0) };          handle_return_value(r)      }      pub fn stop_tx(&self) -> Result<(), FL2KError> {          handle_return_value( unsafe { fl2k_stop_tx(self.device) } )      } + +    pub fn send(&self, r_buf: Vec<i8>, g_buf: Vec<i8>) -> bool { +        if r_buf.len() != g_buf.len() { +            panic!("r_buf and g_buf must have same length"); +        } +        self.tx.send((r_buf, g_buf)).is_ok() +    }  } diff --git a/src/main.rs b/src/main.rs index c04740f..76808e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,36 +31,36 @@ use getopts::Options;  mod fl2k; -const TRIG_TABLE_ORDER : usize = 8; -const TRIG_TABLE_SHIFT : usize = 32 - TRIG_TABLE_ORDER; -const TRIG_TABLE_LEN : usize = 1 << TRIG_TABLE_ORDER; +const TRIG_TABLE_ORDER: usize = 8; +const TRIG_TABLE_SHIFT: usize = 32 - TRIG_TABLE_ORDER; +const TRIG_TABLE_LEN: usize = 1 << TRIG_TABLE_ORDER; -const PI : f32 = std::f32::consts::PI; -const INT32_MAX_AS_FLOAT : f32 = 0x1_0000_0000u64 as f32; -const ANG_INCR : f32 = INT32_MAX_AS_FLOAT / (2.0 * PI); +const PI: f32 = std::f32::consts::PI; +const INT32_MAX_AS_FLOAT: f32 = 0x1_0000_0000u64 as f32; +const ANG_INCR: f32 = INT32_MAX_AS_FLOAT / (2.0 * PI);  enum Waveform { Sine, Rect }  enum Output { Debug, FL2K }  struct DDS { -    trig_table_quadrature : Vec<i16>, -    trig_table_inphase : Vec<i16>, +    trig_table_quadrature: Vec<i16>, +    trig_table_inphase: Vec<i16>,      /* instantaneous phase */ -    phase : u32, +    phase: u32,      /* phase increment */ -    phase_step : u32, +    phase_step: u32,      /* for phase modulation */ -    phase_delta : i32, -    phase_slope : i32, +    phase_delta: i32, +    phase_slope: i32, -    amplitude : f32, +    amplitude: f32,  }  impl DDS { -    fn init(samp_rate : f32, freq : f32, phase : f32, amp : f32, waveform : Waveform) -> Self { +    fn init(samp_rate: f32, freq: f32, phase: f32, amp: f32, waveform: Waveform) -> Self {          let mut trig_table_inphase = Vec::with_capacity(TRIG_TABLE_LEN);          let mut trig_table_quadrature = Vec::with_capacity(TRIG_TABLE_LEN); @@ -94,13 +94,14 @@ impl DDS {          }      } -    fn set_phase(&mut self, phase_delta : i32, phase_slope : i32) { +    fn set_phase(&mut self, phase_delta: i32, phase_slope: i32) {          self.phase_delta = phase_delta;          self.phase_slope = phase_slope;      } -    fn generate_iq(&mut self, len : usize) -> Vec<(i8, i8)> { -        let mut out = Vec::with_capacity(len); +    fn generate_iq(&mut self, len: usize) -> (Vec<i8>, Vec<i8>) { +        let mut out_i = Vec::with_capacity(len); +        let mut out_q = Vec::with_capacity(len);          for _ in 0..len {              let phase = self.phase as i32;              // get current carrier phase, add phase mod, calculate table index @@ -119,11 +120,12 @@ impl DDS {                                                                                         //              let i = (amp_i >> 24) as i8;        // 0..31 >> 24 => 0..8              let q = (amp_q >> 24) as i8;        // 0..31 >> 24 => 0..8 -            out.push((i, q)); +            out_i.push(i); +            out_q.push(q);              self.phase_delta  += self.phase_slope;          } -        out +        (out_i, out_q)      }  } @@ -168,12 +170,12 @@ fn main() {          Output::FL2K      }; -    let device_index : u32 = match cli_args.opt_str("d") { +    let device_index: u32 = match cli_args.opt_str("d") {          Some(s) => s.parse().expect("integer value"),          None => 0,      }; -    let samp_rate : u32 = match cli_args.opt_str("s") { +    let samp_rate: u32 = match cli_args.opt_str("s") {          Some(s) => s.parse().expect("integer value"),          None => 96_000_000,      }; @@ -183,7 +185,7 @@ fn main() {          None => 1_440_000.0,      }; -    let input_freq : u32 = match cli_args.opt_str("i") { +    let input_freq: u32 = match cli_args.opt_str("i") {          Some(s) => s.parse().expect("integer value"),          None => 48_000,      }; @@ -235,7 +237,7 @@ fn main() {      let source_file = File::open(source_file_name).expect("open file");      let mut source_file = BufReader::new(source_file); -    const BASEBAND_BUF_SAMPS : usize = 1024; +    const BASEBAND_BUF_SAMPS: usize = 1024;      // Read file and convert samples      thread::spawn(move || { @@ -252,9 +254,9 @@ fn main() {              source_file.read_exact(&mut buf_u8).expect("Read from source file"); -            let buf : Vec<f32> = buf +            let buf: Vec<f32> = buf                  .iter() -                .map(|v| (v/2 + i16::MAX) as f32 / 32768.0) +                .map(|v| (v/2 + i16::MAX/2) as f32 / 32768.0)                  .collect();              if let Err(_) = input_samples_tx.send(buf) { @@ -285,8 +287,8 @@ fn main() {                  let pd = lastamp * modulation_index * INT32_MAX_AS_FLOAT; -                const MIN_VAL : f32 = std::i32::MIN as f32; -                const MAX_VAL : f32 = std::i32::MAX as f32; +                const MIN_VAL: f32 = std::i32::MIN as f32; +                const MAX_VAL: f32 = std::i32::MAX as f32;                  if pd < MIN_VAL || pd > MAX_VAL {                      panic!("pd out of bounds {}", pd); @@ -318,9 +320,9 @@ fn main() {              for (pd, pdslope) in buf {                  dds.set_phase(pd, pdslope); -                let iq_buf = dds.generate_iq(rf_to_baseband_sample_ratio as usize); +                let iq_bufs = dds.generate_iq(rf_to_baseband_sample_ratio as usize); -                if let Err(_) = iq_tx.send(iq_buf) { +                if let Err(_) = iq_tx.send(iq_bufs) {                      eprintln!("Quit dds thread");                      break;                  } @@ -331,14 +333,35 @@ fn main() {      // Main thread, output to file/device      match output {          Output::FL2K => { -            let fl2k = fl2k::FL2K::open(device_index).expect("fl2k open"); -            // TODO fl2k.start_tx(|| {}, 0).expect("fl2k start_tx"); +            let mut fl2k = fl2k::FL2K::open(device_index).expect("fl2k open"); +            fl2k.start_tx().expect("fl2k start_tx"); + +            fl2k.set_sample_rate(samp_rate).expect("set fl2k sample rate"); + +            eprintln!("FL2K sample ret set to {}", fl2k.get_sample_rate().unwrap()); + +            loop { +                let Ok((i, q)) = iq_rx.recv() else { break }; +                if fl2k.send(i, q) == false { break }; +            } + +            fl2k.stop_tx().expect("stop tx");          }          Output::Debug => {              let out_file = File::create("debug-out.i8").expect("create file");              let mut out_file = BufWriter::new(out_file);              loop { -                let Ok(buf) = iq_rx.recv() else { break }; +                let Ok((i_buf, q_buf)) = iq_rx.recv() else { break }; + +                if i_buf.len() != q_buf.len() { +                    panic!("i_buf and q_buf must have same length"); +                } + +                let mut buf = Vec::with_capacity(i_buf.len() * 2); +                for (i, q) in i_buf.iter().zip(q_buf) { +                    buf.push(*i); +                    buf.push(q); +                }                  let buf_u8: &[u8] = unsafe {                      std::slice::from_raw_parts(  | 
