use super::xorshift::XorShift32;
use crate::core::fixed::fixed::Q15;
use crate::core::fixed::tables::sine;
use crate::core::fixed::units::Phase;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Eq, Hash, PartialEq)]
pub enum Waveform {
#[default]
TranslatedSine,
TranslatedSquare,
TranslatedRampUp,
TranslatedRampDown,
Sine,
RampDown,
Square,
Random,
}
impl Waveform {
pub fn value_q15(&self, phase: Phase) -> Q15 {
match self {
Waveform::TranslatedSine => sine(phase.shifted(Phase::QUARTER)).halved() + Q15::HALF,
Waveform::TranslatedSquare => {
if phase.is_first_half() {
Q15::ONE
} else {
Q15::ZERO
}
}
Waveform::TranslatedRampUp => phase.shifted(Phase::HALF).to_q15_unsigned(),
Waveform::TranslatedRampDown => Q15::ONE - Waveform::TranslatedRampUp.value_q15(phase),
Waveform::Sine => -sine(phase),
Waveform::RampDown => Q15::from_raw(phase.raw_as_i16().wrapping_neg()),
Waveform::Square => {
if phase.is_first_half() {
Q15::NEG_ONE
} else {
Q15::ONE
}
}
Waveform::Random => Q15::ZERO,
}
}
}
#[derive(Default, Clone, Copy, Debug)]
pub struct WaveformState {
wf: Waveform,
rng: XorShift32,
}
impl WaveformState {
pub fn new(wf: Waveform) -> Self {
Self {
wf,
rng: XorShift32::default(),
}
}
#[inline]
pub fn waveform(&self) -> Waveform {
self.wf
}
pub fn value_q15(&mut self, phase: Phase) -> Q15 {
if let Waveform::Random = self.wf {
let n = self.rng.next().unwrap() as i16;
Q15::from_raw(n & Q15::ONE.raw())
} else {
self.wf.value_q15(phase)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn approx_eq_q15(a: Q15, b: Q15, tol: i16) -> bool {
(a.raw() as i32 - b.raw() as i32).abs() <= tol as i32
}
#[test]
fn translated_sine_range() {
const SAMPLES_PER_CYCLE: u32 = 256;
const STEP: u16 = ((1u32 << 16) / SAMPLES_PER_CYCLE) as u16;
let wf = Waveform::TranslatedSine;
for k in 0..SAMPLES_PER_CYCLE {
let phase = Phase::from_raw((k as u16).wrapping_mul(STEP));
let v = wf.value_q15(phase);
assert!(
v.raw() >= 0,
"TranslatedSine should be unsigned, got {} at phase {:#x}",
v.raw(),
phase.raw()
);
}
}
#[test]
fn translated_sine_key_points() {
let wf = Waveform::TranslatedSine;
assert!(approx_eq_q15(wf.value_q15(Phase::ZERO), Q15::ONE, 4));
assert!(approx_eq_q15(wf.value_q15(Phase::QUARTER), Q15::HALF, 4));
assert!(approx_eq_q15(wf.value_q15(Phase::HALF), Q15::ZERO, 4));
assert!(approx_eq_q15(
wf.value_q15(Phase::THREE_QUARTERS),
Q15::HALF,
4
));
}
#[test]
fn translated_ramps_centre_at_phase_zero() {
assert!(approx_eq_q15(
Waveform::TranslatedRampUp.value_q15(Phase::ZERO),
Q15::HALF,
2
));
assert!(approx_eq_q15(
Waveform::TranslatedRampDown.value_q15(Phase::ZERO),
Q15::HALF,
2
));
}
#[test]
fn random_waveform_not_stuck() {
let mut ws = WaveformState::new(Waveform::Random);
let first = ws.value_q15(Phase::ZERO);
let second = ws.value_q15(Phase::ZERO);
assert_ne!(first.raw(), second.raw(), "Random waveform appears stuck");
}
}