diff options
| author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2023-01-03 12:28:08 +0100 | 
|---|---|---|
| committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2023-01-03 12:28:08 +0100 | 
| commit | 38ef609475a25143640bf8d2d1df5ad6a68b7403 (patch) | |
| tree | 2dcd2e4fc289aa21d74b48f70f4f93aabe13f027 /src | |
| parent | 255119ae911e65ed5b579034c1d965d770851a1e (diff) | |
| download | fl2k_ampliphase-38ef609475a25143640bf8d2d1df5ad6a68b7403.tar.gz fl2k_ampliphase-38ef609475a25143640bf8d2d1df5ad6a68b7403.tar.bz2 fl2k_ampliphase-38ef609475a25143640bf8d2d1df5ad6a68b7403.zip  | |
Simplify DDS, add plots
Diffstat (limited to 'src')
| -rw-r--r-- | src/fl2k.rs | 1 | ||||
| -rw-r--r-- | src/main.rs | 170 | 
2 files changed, 109 insertions, 62 deletions
diff --git a/src/fl2k.rs b/src/fl2k.rs index f2e197b..74357b3 100644 --- a/src/fl2k.rs +++ b/src/fl2k.rs @@ -69,6 +69,7 @@ extern "C" fn tx_callback(data_info: *mut fl2k_ampliphase::fl2k_data_info_t) {                  },                  Err(_) => {                      (*ctx).buffer_underflows += 1; +                    eprintln!("FL2K buffers not ready on callback");                  },              }          } diff --git a/src/main.rs b/src/main.rs index 7d052bc..1c6f520 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,11 +41,35 @@ const ANG_INCR: f32 = INT32_MAX_AS_FLOAT / (2.0 * PI);  enum Waveform { Sine, Rect } +#[derive(Clone, Copy)]  enum Output { Debug, FL2K } +struct BufferDumper<T> { +    phantom : std::marker::PhantomData<T>, +    writer : BufWriter<File>, +} + +impl<T> BufferDumper<T> { +    fn new(filename: &str) -> Self { +        let writer = BufWriter::new(File::create(filename).expect("create file")); +        BufferDumper { phantom: std::marker::PhantomData, writer } +    } + +    fn write_buf(&mut self, buf: &[T]) -> std::io::Result<()> { +        let buf_u8: &[u8] = unsafe { +            std::slice::from_raw_parts( +                buf.as_ptr() as *const u8, +                buf.len() * std::mem::size_of::<T>() +            ) +        }; + +        self.writer.write_all(buf_u8) +    } +} +  struct DDS { -    trig_table_quadrature: Vec<i16>, -    trig_table_inphase: Vec<i16>, +    trig_table_quadrature: Vec<i8>, +    trig_table_inphase: Vec<i8>,      /* instantaneous phase */      phase: u32, @@ -55,28 +79,26 @@ struct DDS {      /* for phase modulation */      phase_delta: i32,      phase_slope: i32, - -    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, 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);          let incr = 1.0f32 / TRIG_TABLE_LEN as f32;          for i in 0..TRIG_TABLE_LEN { -            let i = f32::cos(incr * i as f32 * 2.0 * PI) * 32767.0; -            let q = f32::sin(incr * i as f32 * 2.0 * PI) * 32767.8; +            let i = f32::cos(incr * i as f32 * 2.0 * PI) * 255.0; +            let q = f32::sin(incr * i as f32 * 2.0 * PI) * 255.0;              match waveform {                  Waveform::Sine => { -                    trig_table_inphase.push(f32::round(i) as i16); -                    trig_table_quadrature.push(f32::round(q) as i16); +                    trig_table_inphase.push(f32::round(i) as i8); +                    trig_table_quadrature.push(f32::round(q) as i8);                  }                  Waveform::Rect => { -                    trig_table_inphase.push(if i >= 0.0 { 32767 } else { -32767 }); -                    trig_table_quadrature.push(if q >= 0.0 { 32767 } else { -32767 }); +                    trig_table_inphase.push(if i >= 0.0 { 127 } else { -127 }); +                    trig_table_quadrature.push(if q >= 0.0 { 127 } else { -127 });                  }              }          } @@ -90,41 +112,7 @@ impl DDS {              phase_step: f32::round(phase_step) as u32,              phase_delta: 0,              phase_slope: 0, -            amplitude: amp, -        } -    } - -    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>, Vec<i8>) { -        let mut out_i = Vec::with_capacity(len); -        let mut out_q = Vec::with_capacity(len); -        for _ in 0..len { -            // get current carrier phase, add phase mod, calculate table index -            let phase_idx_i = self.phase.overflowing_sub(self.phase_delta as u32).0 >> TRIG_TABLE_SHIFT; -            let phase_idx_q = self.phase.overflowing_add(self.phase_delta as u32).0 >> TRIG_TABLE_SHIFT; - -            if phase_idx_q > 255 || phase_idx_i > 255 { -                panic!("Phase IDX out of bounds"); -            } - -            self.phase = self.phase.overflowing_add(self.phase_step).0; - -            let amp = (self.amplitude * 32767.0) as i32;                               // 0..15 -            let amp_i = amp * self.trig_table_inphase[phase_idx_i as usize] as i32;    // 0..31 -            let amp_q = amp * self.trig_table_quadrature[phase_idx_q as usize] as i32; // 0..31 - -            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_i.push(i); -            out_q.push(q); - -            self.phase_delta += self.phase_slope;          } -        (out_i, out_q)      }  } @@ -184,7 +172,7 @@ fn main() {          None => 1_440_000.0,      }; -    let input_freq: u32 = match cli_args.opt_str("i") { +    let input_rate: u32 = match cli_args.opt_str("i") {          Some(s) => s.parse().expect("integer value"),          None => 48_000,      }; @@ -214,13 +202,14 @@ fn main() {          }      }; -    if samp_rate % input_freq != 0 { -        eprintln!("WARNING: input freq does not divide sample rate."); +    if samp_rate % input_rate != 0 { +        eprintln!("WARNING: input_rate freq does not divide sample rate.");      } -    let rf_to_baseband_sample_ratio = samp_rate / input_freq; +    let rf_to_baseband_sample_ratio = samp_rate / input_rate; -    eprintln!("Samplerate:       {} MHz", (samp_rate as f32)/1000000.0); -    eprintln!("Center frequency: {} kHz", base_freq/1000.0); +    eprintln!("Input rate:       {} kHz", (input_rate as f32)/1e3); +    eprintln!("Samplerate:       {} MHz", (samp_rate as f32)/1e6); +    eprintln!("Center frequency: {} kHz", base_freq/1e3);      let running = Arc::new(AtomicBool::new(true)); @@ -240,6 +229,11 @@ fn main() {      // Read file and convert samples      thread::spawn(move || { +        let mut in_debug = match output { +            Output::Debug => Some(BufferDumper::<f32>::new("debug-in.f32")), +            Output::FL2K => None, +        }; +          while running.load(Ordering::SeqCst) {              let mut buf = Vec::with_capacity(BASEBAND_BUF_SAMPS);              buf.resize(BASEBAND_BUF_SAMPS, 0i16); @@ -251,13 +245,27 @@ fn main() {                  )              }; -            source_file.read_exact(&mut buf_u8).expect("Read from source file"); +            if let Err(e) = source_file.read_exact(&mut buf_u8) { +                if e.kind() != std::io::ErrorKind::UnexpectedEof { +                    eprintln!("Input file read stopped {:?}", e); +                } +                else { +                    eprintln!("Input file EOF reached"); +                } +                break; +            }              let buf: Vec<f32> = buf                  .iter()                  .map(|v| (v/2 + i16::MAX/2) as f32 / 32768.0)                  .collect(); +            if let Some(w) = &mut in_debug { +                if let Err(e) = w.write_buf(&buf) { +                    eprintln!("Error writing debug-in.f32: {}", e); +                } +            } +              if let Err(_) = input_samples_tx.send(buf) {                  eprintln!("Quit read thread");                  break; @@ -268,6 +276,12 @@ fn main() {      // Read samples, calculate PD and PDSLOPE      thread::spawn(move || {          let mut lastamp = 0f32; + +        let mut debug_writer = match output { +            Output::Debug => Some(BufWriter::new(File::create("debug-pd.csv").expect("create file"))), +            Output::FL2K => None, +        }; +          loop {              let Ok(buf) = input_samples_rx.recv() else { break }; @@ -281,8 +295,7 @@ fn main() {                 efficient and pretty good interpolation filter. */              for sample in buf { -                let slope = sample - lastamp; -                let slope = slope / rf_to_baseband_sample_ratio as f32; +                let slope = (sample - lastamp) / rf_to_baseband_sample_ratio as f32;                  let pd = lastamp * modulation_index * INT32_MAX_AS_FLOAT; @@ -298,6 +311,11 @@ fn main() {                      panic!("pdslope out of bounds {}", pdslope);                  } +                if let Some(w) = &mut debug_writer { +                    writeln!(w, "{},{},{},{}", sample, slope, pd, pdslope) +                        .expect("write debug-pd.csv"); +                } +                  pd_buf.push((pd as i32, pdslope as i32));                  lastamp = sample; @@ -312,16 +330,45 @@ fn main() {      // Read PD and PDSLOPE, interpolate to higher rate      thread::spawn(move || { -        let mut dds = DDS::init(samp_rate as f32, base_freq, 0.0, 1.0, waveform); +        let mut dds = DDS::init(samp_rate as f32, base_freq, 0.0, waveform); + +        let mut debug_writer = match output { +            Output::Debug => Some(BufWriter::new(File::create("debug-dds.csv").expect("create file"))), +            Output::FL2K => None, +        }; +          loop {              let Ok(pd_buf) = pd_rx.recv() else { break };              for (pd, pdslope) in pd_buf { -                dds.set_phase(pd, pdslope); - -                let iq_bufs = dds.generate_iq(rf_to_baseband_sample_ratio as usize); +                dds.phase_delta = pd; +                dds.phase_slope = pdslope; + +                let len = rf_to_baseband_sample_ratio as usize; +                let mut out_i = Vec::with_capacity(len); +                let mut out_q = Vec::with_capacity(len); +                for ix in 0..len { +                    // get current carrier phase, add phase mod, calculate table index +                    let phase_idx_i = dds.phase.overflowing_sub(dds.phase_delta as u32).0 >> TRIG_TABLE_SHIFT; +                    let phase_idx_q = dds.phase.overflowing_add(dds.phase_delta as u32).0 >> TRIG_TABLE_SHIFT; + +                    if phase_idx_q > 255 || phase_idx_i > 255 { +                        panic!("Phase IDX out of bounds"); +                    } + +                    out_i.push(dds.trig_table_inphase[phase_idx_i as usize]); +                    out_q.push(dds.trig_table_quadrature[phase_idx_q as usize]); + +                    if let Some(w) = &mut debug_writer { +                        writeln!(w, "{},{},{},{},{}", ix, dds.phase, dds.phase_delta, phase_idx_i, phase_idx_q) +                            .expect("write debug-dds.csv"); +                    } + +                    dds.phase = dds.phase.overflowing_add(dds.phase_step).0; +                    dds.phase_delta += dds.phase_slope; +                } -                if let Err(_) = iq_tx.send(iq_bufs) { +                if let Err(_) = iq_tx.send((out_i, out_q)) {                      eprintln!("Quit dds thread");                      break;                  } @@ -347,8 +394,7 @@ fn main() {              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); +            let mut out_file = BufferDumper::new("debug-out.i8");              loop {                  let Ok((i_buf, q_buf)) = iq_rx.recv() else { break }; @@ -369,7 +415,7 @@ fn main() {                      )                  }; -                if let Err(e) = out_file.write_all(buf_u8) { +                if let Err(e) = out_file.write_buf(buf_u8) {                      eprintln!("Write output error: {}", e);                      break;                  }  | 
