use std::ops::{AddAssign, MulAssign};
use nalgebra::{ComplexField, DMatrix, DVector};
use num_traits::Float;
use crate::{Discrete, TransferFunction};
use crate::system_identification::arx::Arx;
use crate::system_identification::lsm;
pub struct DataBuffer<T> {
pub arx: Arx<T>,
pub auxiliary: Arx<T>,
x_hat: T,
zeta_y_sum: DVector<T>,
zeta_phi_sum: DMatrix<T>,
}
impl<T: Float + AddAssign + MulAssign + ComplexField> DataBuffer<T> {
pub fn new(auxiliary: &Arx<T>) -> Self {
let n = auxiliary.parameter_len();
let mut auxiliary = auxiliary.clone();
auxiliary.clear_history();
let mut arx = auxiliary.clone();
arx.parameter.fill(T::zero());
Self {
arx,
auxiliary,
x_hat: T::zero(),
zeta_y_sum: DVector::zeros(n),
zeta_phi_sum: DMatrix::zeros(n, n),
}
}
pub fn add(&mut self, u: T, x: T, y: T) {
self.arx.push(u, x);
let phi = self.arx.regressor();
self.auxiliary.push(u, self.x_hat);
let zeta = self.auxiliary.regressor();
self.x_hat = self.auxiliary.predict();
self.zeta_y_sum += &zeta * y;
self.zeta_phi_sum += &zeta * &phi.transpose();
}
pub fn identify(&mut self) -> Option<TransferFunction<T, Discrete>> {
self.arx.parameter = self.zeta_phi_sum.clone().lu().solve(&self.zeta_y_sum)?;
Some(self.arx.transfer_function())
}
}
pub fn identify<T>(u: &[T], y: &[T], structure: &Arx<T>, iterations: usize) -> Option<Arx<T>>
where
T: Float + AddAssign + MulAssign + ComplexField,
{
let y_prev = |k: usize| if k > 0 { y[k - 1] } else { T::zero() };
let mut arx = structure.clone();
arx.clear_history();
let mut buffer = lsm::arx::DataBuffer::from_arx(arx);
for (k, (&uk, &yk)) in u.iter().zip(y).enumerate() {
buffer.add(uk, y_prev(k), yk);
}
buffer.identify()?;
let mut model = buffer.arx;
for _ in 0..iterations {
let mut buffer = DataBuffer::new(&model);
for (k, (&uk, &yk)) in u.iter().zip(y).enumerate() {
buffer.add(uk, y_prev(k), yk);
}
buffer.identify()?;
model = buffer.arx;
}
Some(model)
}