aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/fl2k.rs78
-rw-r--r--src/main.rs87
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(