use miniconf::{Leaf, Tree};
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub enum Signal {
Cosine,
Square,
Triangle,
WhiteNoise,
}
#[derive(Copy, Clone, Debug, Tree, Serialize, Deserialize)]
pub struct BasicConfig {
pub signal: Leaf<Signal>,
pub frequency: Leaf<f32>,
pub symmetry: Leaf<f32>,
pub amplitude: Leaf<f32>,
pub phase: Leaf<f32>,
}
impl Default for BasicConfig {
fn default() -> Self {
Self {
frequency: 1.0e3.into(),
symmetry: 0.5.into(),
signal: Signal::Cosine.into(),
amplitude: 0.0.into(),
phase: 0.0.into(),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum Error {
InvalidAmplitude,
InvalidSymmetry,
InvalidFrequency,
}
impl BasicConfig {
pub fn try_into_config(
self,
sample_period: f32,
full_scale: f32,
) -> Result<Config, Error> {
let symmetry_complement = 1.0 - *self.symmetry;
if *self.symmetry < 0.0 || symmetry_complement < 0.0 {
return Err(Error::InvalidSymmetry);
}
const NYQUIST: f32 = (1u32 << 31) as _;
let ftw = *self.frequency * sample_period * NYQUIST;
if ftw < 0.0 || 2.0 * ftw > NYQUIST {
return Err(Error::InvalidFrequency);
}
let phase_increment = [
if *self.symmetry * NYQUIST > ftw {
ftw / *self.symmetry
} else {
NYQUIST
} as i32,
if symmetry_complement * NYQUIST > ftw {
ftw / symmetry_complement
} else {
NYQUIST
} as i32,
];
let amplitude = *self.amplitude * (i16::MIN as f32 / -full_scale);
if !(i16::MIN as f32..=i16::MAX as f32).contains(&litude) {
return Err(Error::InvalidAmplitude);
}
let phase = *self.phase * (1u64 << 32) as f32;
Ok(Config {
amplitude: amplitude as i16,
signal: *self.signal,
phase_increment,
phase_offset: phase as i32,
})
}
}
#[derive(Copy, Clone, Debug)]
pub struct Config {
pub signal: Signal,
pub amplitude: i16,
pub phase_increment: [i32; 2],
pub phase_offset: i32,
}
impl Default for Config {
fn default() -> Self {
Self {
signal: Signal::Cosine,
amplitude: 0,
phase_increment: [0, 0],
phase_offset: 0,
}
}
}
#[derive(Debug)]
pub struct SignalGenerator {
phase_accumulator: i32,
config: Config,
rng: XorShiftRng,
}
impl SignalGenerator {
pub fn new(config: Config) -> Self {
Self {
config,
phase_accumulator: 0,
rng: XorShiftRng::from_seed([0; 16]), }
}
pub fn update_waveform(&mut self, new_config: Config) {
self.config = new_config;
}
pub fn clear_phase_accumulator(&mut self) {
self.phase_accumulator = 0;
}
}
impl core::iter::Iterator for SignalGenerator {
type Item = i16;
fn next(&mut self) -> Option<i16> {
let phase = self
.phase_accumulator
.wrapping_add(self.config.phase_offset);
let sign = phase.is_negative();
self.phase_accumulator = self
.phase_accumulator
.wrapping_add(self.config.phase_increment[sign as usize]);
let scale = match self.config.signal {
Signal::Cosine => idsp::cossin(phase).0 >> 16,
Signal::Square => {
if sign {
i16::MIN as i32
} else {
-(i16::MIN as i32)
}
}
Signal::Triangle => i16::MIN as i32 + (phase >> 15).abs(),
Signal::WhiteNoise => self.rng.next_u32() as i32 >> 16,
};
Some(((self.config.amplitude as i32 * scale) >> 15) as _)
}
}