/* 
 * File:   Equalizer.hpp
 * Author: jens
 *
 * Created on 20. Dezember 2014, 09:24
 */

#ifndef _RADIO_EQUALIZER_HPP_
#define	_RADIO_EQUALIZER_HPP_

#include "Vector.hpp"
#include "FirComplex.hpp"

namespace Radio
{
namespace Equalizer
{

class AEqualizer : public FirComplex
{
public:
	AEqualizer(size_t numTaps)
	: FirComplex(numTaps)
	, m_w(numTaps)
	{
		
	}
	
	virtual ~AEqualizer() {}
	
	void setNumTaps(size_t numTaps)
	{
		FirComplex::setNumTaps(numTaps);
		m_w.resize(numTaps);
	}
	
	void setUnit(size_t delay)
	{
		m_w = ComplexScalar(0, 0);
		m_w[delay] = ComplexScalar(1, 0);
	}

	const CVec& getWeights()
	{
		return m_w;
	}

	const AEqualizer& operator=(const AEqualizer &rhs)
	{
		this->m_w = rhs.m_w;
		return *this;
	}
	
protected:
	CVec m_w;

};

class Lms : public AEqualizer
{
public:
	Lms(size_t numTaps)
	: AEqualizer(numTaps)
	{
		
	}
	~Lms()
	{
		
	}

	void update(ComplexScalar const &e, RealScalar const &mu)
	{
		m_w += m_state * conj(e) * mu;
	}
	
private:
};
	
class Cma : public Lms
{
public: 
	Cma(size_t numTaps=0)
	: Lms(numTaps)
	, m_xLast(0)
	, m_power(0)
	{
	}
	
	void init(size_t numTaps)
	{
		setNumTaps(numTaps);
	}
	
	ComplexScalar process(ComplexScalar const &x)
	{
		RealScalar x1, x2;
		
		x1 = abs(x);
		x2 = abs(m_state[m_numTaps-1]);
		m_power = std::max(RealScalar(0), m_power + (x1*x1 - m_xLast));
		m_xLast = x2*x2;		
		
		FirComplex::feed(x);
		return FirComplex::process(m_w);
	}
	
	// Proakis: page 698
	void trainGodard(ComplexScalar const &sym_s_eq, RealScalar const &mag_h, RealScalar R2, RealScalar mu)
	{
		ComplexScalar d;
		ComplexScalar e;
		RealScalar mag_s, t;

		mag_s = abs(sym_s_eq);

		t = (RealScalar)((mag_h + R2*mag_s - mag_s*mag_s*mag_s)/(1E-4+mag_h));
		d = sym_s_eq * t;

		e = d - sym_s_eq;
		mu /= (1+m_power);
		
		Lms::update(e, mu);
	}

	// "Sliced Multi-modulus Blind Equalization Algorithm",
	// ETRI Journal, Volume 27, Number 3, June 2005,
	// Shafayat Abrar and Roy A. Axford Jr.
	void trainMma(ComplexScalar const &sym_s_eq, ComplexScalar const &R, RealScalar mu)
	{
		ComplexScalar e(sym_s_eq.real()*(R.real() - sym_s_eq.real()*sym_s_eq.real()), sym_s_eq.imag()*(R.imag() - sym_s_eq.imag()*sym_s_eq.imag()));

		mu /= (1+m_power);
		Lms::update(e, mu);
	}

	// "Sliced Multi-modulus Blind Equalization Algorithm",
	// ETRI Journal, Volume 27, Number 3, June 2005,
	// Shafayat Abrar and Roy A. Axford Jr.
	void trainSmma(ComplexScalar const &sym_s_eq, RealScalar const &mag_h, ComplexScalar const &R, RealScalar mu)
	{
		ComplexScalar e(sym_s_eq.real()*(mag_h*R.real() - sym_s_eq.real()*sym_s_eq.real()), sym_s_eq.imag()*(mag_h*R.imag() - sym_s_eq.imag()*sym_s_eq.imag()));

		mu /= (1+m_power);
		Lms::update(e, mu);
	}
	
private:
	RealScalar m_xLast;
	RealScalar m_power;
};

class Dfe : public Lms
{
public:
	Dfe(size_t k=0)
	: Lms(2*k+1)
	, m_numFeedforward(k+1)
	, m_numFeedBack(k)
	{
		
	}
	
	~Dfe()
	{
		
	}
	void init(size_t k)
	{
		setNumTaps(2*k+1);
		m_numFeedforward = k+1;
		m_numFeedBack = k;
	}

	ComplexScalar process(ComplexScalar const &xs, ComplexScalar const &xh)
	{
		// P-Code
		// x(0:size-1) = [xs, x(0:m_numFeedforward-2), xh, x(m_numFeedforward:size-2)]
		auto sv_xs = subvector(m_state, 0, m_numFeedforward);
		insert_left(sv_xs, xs);

		auto sv_xh = subvector(m_state, m_numFeedforward, m_numFeedBack);
		insert_left(sv_xh, xh);

		return FirComplex::process(m_w);
	}

	void train(ComplexScalar e, RealScalar mu)
	{
		Lms::update(e, mu);
	}
	
private:
	size_t m_numFeedforward;
	size_t m_numFeedBack;
	
};

} // Radio::Equalizer
} // ::Radio


#endif	/* _RADIO_EQUALIZER_HPP_ */

