use std::marker::PhantomData;
use nalgebra::{ComplexField, RealField};
use num_traits::Float;
use crate::{DiscreteSystem, Siso};
use super::ExcitationError;
const TAPS: [&[usize]; 31] = [
&[2, 1],
&[3, 2],
&[4, 3],
&[5, 3],
&[6, 5],
&[7, 6],
&[8, 6, 5, 4],
&[9, 5],
&[10, 7],
&[11, 9],
&[12, 11, 10, 4],
&[13, 12, 11, 8],
&[14, 13, 12, 2],
&[15, 14],
&[16, 15, 13, 4],
&[17, 14],
&[18, 11],
&[19, 18, 17, 14],
&[20, 17],
&[21, 19],
&[22, 21],
&[23, 18],
&[24, 23, 22, 17],
&[25, 22],
&[26, 6, 2, 1],
&[27, 5, 2, 1],
&[28, 25],
&[29, 27],
&[30, 6, 4, 1],
&[31, 28],
&[32, 22, 2, 1],
];
#[derive(Clone, Debug)]
pub struct MSequence<T> {
order: usize,
clock: usize,
taps: &'static [usize],
state: u64,
held: usize,
period: usize,
_value: PhantomData<T>,
}
impl<T: Float> MSequence<T> {
pub fn new(clock: usize, order: usize) -> Result<Self, ExcitationError> {
if !(2..=32).contains(&order) {
return Err(ExcitationError::InvalidOrder { order });
}
let bits = (1u64 << order) - 1;
let period = usize::try_from(bits).ok()
.and_then(|bits| bits.checked_mul(clock))
.filter(|_| clock > 0).ok_or(ExcitationError::InvalidClock { clock, order })?;
Ok(Self { order, clock, taps: TAPS[order - 2], state: bits, held: 0, period, _value: PhantomData })
}
pub fn step(&mut self) -> T {
if self.step_bit() { T::one() } else { -T::one() }
}
pub fn step_bit(&mut self) -> bool {
let bit = self.state & 1 == 1;
self.held += 1;
if self.held == self.clock {
self.held = 0;
let feedback = self.taps.iter().fold(0, |acc, &t| acc ^ ((self.state >> (self.order - t)) & 1));
self.state = (self.state >> 1) | (feedback << (self.order - 1));
}
bit
}
pub fn period(&self) -> usize {
self.period
}
pub fn reset(&mut self) {
self.state = (1u64 << self.order) - 1;
self.held = 0;
}
}
impl<T: Float> Iterator for MSequence<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
Some(self.step())
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, None)
}
}
#[derive(Clone, Debug)]
pub struct ShapedMSequence<T> {
sequence: MSequence<T>,
filter: DiscreteSystem<T, Siso>,
}
impl<T: Float + ComplexField + RealField> ShapedMSequence<T> {
pub fn new(clock: usize, order: usize, filter: impl Into<DiscreteSystem<T, Siso>>) -> Result<Self, ExcitationError> {
let sequence = MSequence::new(clock, order)?;
let mut filter = filter.into();
filter.reset();
Ok(Self { sequence, filter })
}
pub fn step(&mut self) -> T {
self.filter.update(self.sequence.step())
}
pub fn period(&self) -> usize {
self.sequence.period()
}
pub fn reset(&mut self) {
self.sequence.reset();
self.filter.reset();
}
}