Adding signal generator refactor
This commit is contained in:
parent
68859c387a
commit
24c0075b7a
|
@ -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],
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue