Adding signal generator refactor

This commit is contained in:
Ryan Summers 2021-06-28 14:11:52 +02:00
parent 68859c387a
commit 24c0075b7a
3 changed files with 69 additions and 45 deletions

View File

@ -40,7 +40,7 @@ pub struct Settings {
force_hold: bool, force_hold: bool,
telemetry_period: u16, telemetry_period: u16,
stream_target: StreamTarget, stream_target: StreamTarget,
signal_generator: signal_generator::Config, signal_generator: signal_generator::BasicConfig,
output_mode: [OutputMode; 2], output_mode: [OutputMode; 2],
} }
@ -68,7 +68,7 @@ impl Default for Settings {
// The default telemetry period in seconds. // The default telemetry period in seconds.
telemetry_period: 10, telemetry_period: 10,
signal_generator: signal_generator::Config::default(), signal_generator: signal_generator::BasicConfig::default(),
stream_target: StreamTarget::default(), stream_target: StreamTarget::default(),
output_mode: [OutputMode::IirFilter, OutputMode::IirFilter], output_mode: [OutputMode::IirFilter, OutputMode::IirFilter],

View File

@ -158,9 +158,16 @@ const APP: () = {
telemetry: TelemetryBuffer::default(), telemetry: TelemetryBuffer::default(),
signal_generator: signal_generator::SignalGenerator::new( signal_generator: signal_generator::SignalGenerator::new(
signal_generator::Config { signal_generator::Config {
period: design_parameters::SAMPLE_BUFFER_SIZE as u32, // Same frequency as batch size.
symmetry: 0.5, // TODO: Is this off-by-one?
amplitude: 1.0, frequency_tuning_word: u32::MAX
/ design_parameters::SAMPLE_BUFFER_SIZE as u32,
// Equal symmetry
phase_symmetry: 0,
// 1V Amplitude
amplitude: ((1.0 / 10.24) * i16::MAX as f32) as i16,
signal: signal_generator::Signal::Triangle, signal: signal_generator::Signal::Triangle,
}, },
), ),

View File

@ -1,4 +1,4 @@
use crate::hardware::dac::DacCode; use crate::hardware::{dac::DacCode, design_parameters::ADC_SAMPLE_TICKS};
use miniconf::Miniconf; use miniconf::Miniconf;
use serde::Deserialize; use serde::Deserialize;
@ -10,18 +10,17 @@ pub enum Signal {
} }
#[derive(Copy, Clone, Debug, Miniconf, Deserialize)] #[derive(Copy, Clone, Debug, Miniconf, Deserialize)]
pub struct Config { pub struct BasicConfig {
// TODO: Should period be specified in Hz? pub frequency: f32,
pub period: u32,
pub symmetry: f32, pub symmetry: f32,
pub signal: Signal, pub signal: Signal,
pub amplitude: f32, pub amplitude: f32,
} }
impl Default for Config { impl Default for BasicConfig {
fn default() -> Self { fn default() -> Self {
Self { Self {
period: 10, frequency: 1.0e3,
symmetry: 0.5, symmetry: 0.5,
signal: Signal::Sine, signal: Signal::Sine,
amplitude: 1.0, amplitude: 1.0,
@ -29,8 +28,12 @@ impl Default for Config {
} }
} }
impl Into<InternalConf> for Config { impl Into<Config> for BasicConfig {
fn into(self) -> InternalConf { fn into(self) -> Config {
// Calculate the number of output codes in the signal period.
let period =
(100.0_e6 / (self.frequency * ADC_SAMPLE_TICKS as f32)) as u32;
// Clamp amplitude and symmetry. // Clamp amplitude and symmetry.
let amplitude = if self.amplitude > 10.24 { let amplitude = if self.amplitude > 10.24 {
10.24 10.24
@ -46,41 +49,44 @@ impl Into<InternalConf> for Config {
self.symmetry self.symmetry
}; };
InternalConf { Config {
signal: self.signal, signal: self.signal,
period: self.period,
amplitude: ((amplitude / 10.24) * i16::MAX as f32) as i16, amplitude: ((amplitude / 10.24) * i16::MAX as f32) as i16,
phase_symmetry: (2.0 * (symmetry - 0.5) * i32::MAX as f32) as i32, phase_symmetry: (2.0 * (symmetry - 0.5) * i32::MAX as f32) as i32,
phase_step: ((u32::MAX as u64 + 1u64) / self.period as u64) as u32, frequency_tuning_word: ((u32::MAX as u64 + 1u64) / period as u64)
as u32,
} }
} }
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
struct InternalConf { pub struct Config {
period: u32, // The type of signal being generated
signal: Signal, pub signal: Signal,
amplitude: i16,
// The full-scale output code of the signal
pub amplitude: i16,
// The 32-bit representation of the phase symmetry. That is, with a 50% symmetry, this is equal // The 32-bit representation of the phase symmetry. That is, with a 50% symmetry, this is equal
// to 0. // to 0.
phase_symmetry: i32, pub phase_symmetry: i32,
phase_step: u32, // The frequency tuning word of the signal. Phase is incremented by this amount
pub frequency_tuning_word: u32,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SignalGenerator { pub struct SignalGenerator {
index: u32, phase_accumulator: u32,
config: InternalConf, config: Config,
pending_config: Option<InternalConf>, pending_config: Option<Config>,
} }
impl Default for SignalGenerator { impl Default for SignalGenerator {
fn default() -> Self { fn default() -> Self {
Self { Self {
config: Config::default().into(), config: BasicConfig::default().into(),
index: 0, phase_accumulator: 0,
pending_config: None, pending_config: None,
} }
} }
@ -94,11 +100,11 @@ impl SignalGenerator {
/// ///
/// # Returns /// # Returns
/// The generator /// The generator
pub fn new(config: Config) -> Self { pub fn new(config: impl Into<Config>) -> Self {
Self { Self {
config: config.into(), config: config.into(),
pending_config: None, pending_config: None,
index: 0, phase_accumulator: 0,
} }
} }
@ -112,28 +118,42 @@ impl SignalGenerator {
} }
} }
/// Skip `count` elements of the generator // Increment the phase of the signal.
pub fn skip(&mut self, count: u32) { //
let index = self.index.wrapping_add(count); // # Note
// This handles automatically applying pending configurations on phase wrap.
//
// # Returns
// The new phase to use
fn increment(&mut self) -> i32 {
let (phase, overflow) = self
.phase_accumulator
.overflowing_add(self.config.frequency_tuning_word);
// If we skip past the period of the signal, apply any pending config. self.phase_accumulator = phase;
if index > self.config.period {
// Special case: If the FTW is specified as zero, we would otherwise never update the
// settings. Perform a check here for this corner case.
if overflow || self.config.frequency_tuning_word == 0 {
if let Some(config) = self.pending_config.take() { if let Some(config) = self.pending_config.take() {
self.config = config; self.config = config;
self.phase_accumulator = 0;
} }
} }
self.index = index % self.config.period; self.phase_accumulator as i32
}
/// Skip `count` elements of the generator
pub fn skip(&mut self, count: u32) {
for _ in 0..count {
self.increment();
}
} }
/// Get the next value in the generator sequence. /// Get the next value in the generator sequence.
pub fn next(&mut self) -> i16 { pub fn next(&mut self) -> i16 {
// When phase wraps, apply any new settings. let phase = self.increment();
if self.pending_config.is_some() && self.index == 0 {
self.config = self.pending_config.take().unwrap();
}
let phase = (self.index * self.config.phase_step) as i32;
let amplitude = match self.config.signal { let amplitude = match self.config.signal {
Signal::Sine => (dsp::cossin(phase).1 >> 16) as i16, Signal::Sine => (dsp::cossin(phase).1 >> 16) as i16,
@ -182,9 +202,6 @@ impl SignalGenerator {
} }
}; };
// Update the current index.
self.index = self.index.wrapping_add(1) % self.config.period;
// Calculate the final output result as an i16. // Calculate the final output result as an i16.
let result = amplitude as i32 * self.config.amplitude as i32; let result = amplitude as i32 * self.config.amplitude as i32;
@ -196,7 +213,7 @@ impl SignalGenerator {
/// ///
/// # Note /// # Note
/// Changes will not take effect until the current waveform period elapses. /// Changes will not take effect until the current waveform period elapses.
pub fn update_waveform(&mut self, new_config: Config) { pub fn update_waveform(&mut self, new_config: impl Into<Config>) {
self.pending_config = Some(new_config.into()); self.pending_config = Some(new_config.into());
} }
} }